<?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: Python-T Point</title>
    <description>The latest articles on DEV Community by Python-T Point (@ptp2308).</description>
    <link>https://dev.to/ptp2308</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3897415%2F947cff1d-5bff-4dd6-83d3-b0e7f289f4d4.png</url>
      <title>DEV Community: Python-T Point</title>
      <link>https://dev.to/ptp2308</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ptp2308"/>
    <language>en</language>
    <item>
      <title>💡 MySQL INNER JOIN vs LEFT JOIN — which one should you actually use?</title>
      <dc:creator>Python-T Point</dc:creator>
      <pubDate>Sat, 16 May 2026 03:36:05 +0000</pubDate>
      <link>https://dev.to/ptp2308/mysql-inner-join-vs-left-join-which-one-should-you-actually-use-8aj</link>
      <guid>https://dev.to/ptp2308/mysql-inner-join-vs-left-join-which-one-should-you-actually-use-8aj</guid>
      <description>&lt;h2&gt;
  
  
  ❓ When should you use &lt;em&gt;INNER JOIN&lt;/em&gt; vs &lt;em&gt;LEFT JOIN&lt;/em&gt; in MySQL?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe7xutf7wpxg2gegce4uc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe7xutf7wpxg2gegce4uc.png" alt="mysql inner join vs left join" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The difference between &lt;strong&gt;MySQL INNER JOIN vs LEFT JOIN&lt;/strong&gt; is defined by result set completeness. Use &lt;em&gt;INNER JOIN&lt;/em&gt; to return only rows with matches in both tables. Use &lt;em&gt;LEFT JOIN&lt;/em&gt; to preserve all rows from the left table, filling in &lt;code&gt;NULL&lt;/code&gt; for missing data on the right. Your choice directly determines which records appear — and which disappear.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;📑 Table of Contents&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❓ When should you use &lt;em&gt;INNER JOIN&lt;/em&gt; vs &lt;em&gt;LEFT JOIN&lt;/em&gt; in MySQL?&lt;/li&gt;
&lt;li&gt;🧠 INNER JOIN — Only Matching Rows &lt;em&gt;Survive&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;🔍 LEFT JOIN — Keep All From the &lt;em&gt;Left&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;💡 Real Use Case: Reporting on Inactive Customers&lt;/li&gt;
&lt;li&gt;⚠️ Gotcha: Filtering in ON vs WHERE&lt;/li&gt;
&lt;li&gt;⚡ Performance: INNER JOIN vs LEFT JOIN&lt;/li&gt;
&lt;li&gt;📊 When to Use Each: Decision Framework&lt;/li&gt;
&lt;li&gt;✅ Use INNER JOIN When:&lt;/li&gt;
&lt;li&gt;✅ Use LEFT JOIN When:&lt;/li&gt;
&lt;li&gt;🔁 Example: Monthly Sales Report with Zeros&lt;/li&gt;
&lt;li&gt;🟩 Final Thoughts&lt;/li&gt;
&lt;li&gt;❓ Frequently Asked Questions&lt;/li&gt;
&lt;li&gt;Can LEFT JOIN return more rows than the left table?&lt;/li&gt;
&lt;li&gt;Is INNER JOIN faster than LEFT JOIN?&lt;/li&gt;
&lt;li&gt;What happens if I use WHERE with a NULL check after LEFT JOIN?&lt;/li&gt;
&lt;li&gt;📚 References &amp;amp; Further Reading&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🧠 INNER JOIN — Only Matching Rows &lt;em&gt;Survive&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;An &lt;strong&gt;INNER JOIN&lt;/strong&gt; returns rows where the join condition evaluates to true. Any row in the left or right table without a match is excluded. This behavior follows relational algebra’s intersection semantics: output is limited to overlapping key values.&lt;/p&gt;

&lt;p&gt;MySQL processes the join by evaluating the &lt;code&gt;ON&lt;/code&gt; condition across candidate row pairs. With indexes on join columns, this typically uses indexed lookups — often B-trees — reducing the cost from O(n×m) to O(n log m) or better. Without such indexes, a full Cartesian product may be scanned, degrading performance sharply.&lt;/p&gt;

&lt;p&gt;Consider a bookstore schema with &lt;code&gt;books&lt;/code&gt; and &lt;code&gt;authors&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE authors (
    author_id INT PRIMARY KEY,
    name VARCHAR(100)
);

CREATE TABLE books (
    book_id INT PRIMARY KEY,
    title VARCHAR(200),
    author_id INT,
    FOREIGN KEY (author_id) REFERENCES authors(author_id)
);



INSERT INTO authors VALUES 
(1, 'J.K. Rowling'),
(2, 'George Orwell'),
(3, 'Harper Lee');

INSERT INTO books VALUES 
(101, 'Harry Potter and the Sorcerer Stone', 1),
(102, '1984', 2),
(103, 'To Kill a Mockingbird', 3),
(104, 'Animal Farm', 2);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Querying with &lt;strong&gt;INNER JOIN&lt;/strong&gt; :&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT b.title, a.name 
FROM books b
INNER JOIN authors a ON b.author_id = a.author_id;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Output:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+------------------------------------+---------------+
| title                              | name          |
+------------------------------------+---------------+
| Harry Potter and the Sorcerer Stone| J.K. Rowling  |
| 1984                               | George Orwell |
| To Kill a Mockingbird              | Harper Lee    |
| Animal Farm                        | George Orwell |
+------------------------------------+---------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If a book had &lt;code&gt;author_id = 999&lt;/code&gt; — no matching primary key in &lt;code&gt;authors&lt;/code&gt; — that row would be excluded. Foreign key constraints help prevent such orphans, but they are not required for the query to run.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;INNER JOIN assumes referential integrity. When that assumption fails, data vanishes without error. For reporting or discovery queries, this silence can mislead.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🔍 LEFT JOIN — Keep All From the &lt;em&gt;Left&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;LEFT JOIN&lt;/strong&gt; includes every row from the left table. For each, it appends matching rows from the right. If no match exists, the right-side columns are set to &lt;code&gt;NULL&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is necessary when completeness from the primary entity matters — for example, listing all customers in a retention report, even those with zero activity.&lt;/p&gt;

&lt;h3&gt;
  
  
  💡 Real Use Case: Reporting on Inactive Customers
&lt;/h3&gt;

&lt;p&gt;Given &lt;code&gt;customers&lt;/code&gt; and &lt;code&gt;orders&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE customers (
    customer_id INT PRIMARY KEY,
    name VARCHAR(100)
);

CREATE TABLE orders (
    order_id INT PRIMARY KEY,
    customer_id INT,
    amount DECIMAL(10,2),
    order_date DATE
);



INSERT INTO customers VALUES 
(1, 'Alice'),
(2, 'Bob'),
(3, 'Charlie');

INSERT INTO orders VALUES 
(1001, 1, 299.99, '2023-11-05'),
(1002, 1, 89.50, '2023-12-18'),
(1003, 2, 150.00, '2024-01-10');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To find customers with no orders:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT c.name, o.order_id
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
WHERE o.order_id IS NULL;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Output:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+---------+----------+
| name    | order_id |
+---------+----------+
| Charlie |     NULL |
+---------+----------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;WHERE o.order_id IS NULL&lt;/code&gt; filters for unmatched rows. Since &lt;code&gt;order_id&lt;/code&gt; is &lt;code&gt;NOT NULL&lt;/code&gt; by definition (as PRIMARY KEY), &lt;code&gt;NULL&lt;/code&gt; here means: “no row from &lt;code&gt;orders&lt;/code&gt; was joined.” This pattern is reliable for detecting absence.&lt;/p&gt;

&lt;h3&gt;
  
  
  ⚠️ Gotcha: Filtering in ON vs WHERE
&lt;/h3&gt;

&lt;p&gt;Conditions on the right table behave differently depending on placement. (Also read: &lt;a href="https://pythontpoint.in/jenkins-vs-github-actions-india-which-one-should-you/" rel="noopener noreferrer"&gt;⚙️ Jenkins vs GitHub Actions India — which one should you actually use?&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Filtering in &lt;code&gt;ON&lt;/code&gt;: (Also read: &lt;a href="https://pythontpoint.in/virtualbox-vs-vmware-python-development-which-one-actually/" rel="noopener noreferrer"&gt;🐍 VirtualBox vs VMware Python development — which one actually fits your workflow?&lt;/a&gt;) &lt;em&gt;(More on&lt;a href="https://pythontpoint.in" rel="noopener noreferrer"&gt;PythonTPoint tutorials&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT c.name, o.amount
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id AND o.amount &amp;gt; 200;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Output:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+---------+--------+
| name    | amount |
+---------+--------+
| Alice   | 299.99 |
| Bob     |   NULL |
| Charlie |   NULL |
+---------+--------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;o.amount &amp;gt; 200&lt;/code&gt; condition is part of the join logic. Bob’s $150 order doesn’t match, so no row is joined — but Bob still appears. This preserves the LEFT JOIN semantics.&lt;/p&gt;

&lt;p&gt;Move the condition to &lt;code&gt;WHERE&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT c.name, o.amount
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
WHERE o.amount &amp;gt; 200;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Output:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+-------+--------+
| name  | amount |
+-------+--------+
| Alice | 299.99 |
+-------+--------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now, Bob and Charlie are excluded because &lt;code&gt;NULL &amp;gt; 200&lt;/code&gt; evaluates to &lt;code&gt;UNKNOWN&lt;/code&gt;, which fails the &lt;code&gt;WHERE&lt;/code&gt; filter. The result is functionally identical to an &lt;strong&gt;INNER JOIN&lt;/strong&gt; with that condition. This trap is common in dashboards and aggregations.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚡ Performance: INNER JOIN vs LEFT JOIN
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;INNER JOIN&lt;/strong&gt; typically performs better than &lt;strong&gt;LEFT JOIN&lt;/strong&gt; because the optimizer can reorder joins, eliminate unreachable tables, and apply early filtering. These optimizations rely on the mutual dependency of both tables’ presence.&lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;INNER JOIN&lt;/strong&gt; , indexed lookups on join columns (e.g., B-tree index on &lt;code&gt;orders.customer_id&lt;/code&gt;) allow MySQL to resolve matches in logarithmic time. The query plan can use &lt;code&gt;ref&lt;/code&gt; or &lt;code&gt;eq_ref&lt;/code&gt; access types efficiently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LEFT JOIN&lt;/strong&gt; disables some of these optimizations. The full left table must be read — often via &lt;code&gt;index&lt;/code&gt; or &lt;code&gt;ALL&lt;/code&gt; scan — because every row must appear in the output. For large left tables, this becomes a bottleneck if the right-side index is missing.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;EXPLAIN SELECT c.name, o.amount
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Output:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+------+-------------+----------+--------+---------------+---------+---------+-------------------------+------+-------------+
| id   | select_type | table    | type   | possible_keys | key     | key_len | ref                     | rows | Extra       |
+------+-------------+----------+--------+---------------+---------+---------+-------------------------+------+-------------+
|    1 | SIMPLE      | c        | index  | PRIMARY       | PRIMARY | 4       | NULL                    |    3 | Using index |
|    1 | SIMPLE      | o        | ref    | customer_id   | cust_id | 5       | test.c.customer_id      |    1 | Using where |
+------+-------------+----------+--------+---------------+---------+---------+-------------------------+------+-------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Note: &lt;code&gt;type: index&lt;/code&gt; on &lt;code&gt;customers&lt;/code&gt; means a full index scan. Even though the table is small, this scales linearly. For &lt;code&gt;LEFT JOIN&lt;/code&gt;, the optimizer cannot skip any rows from the left side.&lt;/p&gt;

&lt;p&gt;To prevent performance decay on larger datasets: (Also read: &lt;a href="https://pythontpoint.in/python-pip-vs-pipenv-vs-poetry-which-one-should-you/" rel="noopener noreferrer"&gt;🐍 python pip vs pipenv vs poetry — which one should you actually use?&lt;/a&gt;)&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ALTER TABLE orders ADD INDEX idx_customer_id (customer_id);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Without this index, MySQL may perform a full table scan of &lt;code&gt;orders&lt;/code&gt; for every row in &lt;code&gt;customers&lt;/code&gt;, resulting in O(n×m) cost. With it, lookups stay in O(log m).&lt;/p&gt;




&lt;h2&gt;
  
  
  📊 When to Use Each: Decision Framework
&lt;/h2&gt;

&lt;p&gt;Choose the join type based on data requirements, not convenience.&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ Use INNER JOIN When:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The business logic requires both entities to exist (e.g., invoices must have customers).&lt;/li&gt;
&lt;li&gt;Foreign key constraints guarantee referential integrity.&lt;/li&gt;
&lt;li&gt;Query performance is critical and both tables are large.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ✅ Use LEFT JOIN When:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The left table defines the scope of analysis (e.g., all users, all products).&lt;/li&gt;
&lt;li&gt;Missing related data is meaningful (e.g., inactive accounts, unreviewed items).&lt;/li&gt;
&lt;li&gt;You need to include zero-value aggregations in reports (e.g., monthly sales with $0 months).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🔁 Example: Monthly Sales Report with Zeros
&lt;/h3&gt;

&lt;p&gt;To generate monthly sales per customer, including months with no purchases:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WITH months AS (
  SELECT '2023-01-01' AS month_start UNION ALL
  SELECT '2023-02-01' UNION ALL
  SELECT '2023-03-01' -- ... up to Dec
)
SELECT 
  m.month_start,
  c.name,
  COALESCE(SUM(o.amount), 0) AS monthly_total
FROM months m
CROSS JOIN customers c
LEFT JOIN orders o 
  ON c.customer_id = o.customer_id 
  AND o.order_date &amp;gt;= m.month_start 
  AND o.order_date &amp;lt; DATE_ADD(m.month_start, INTERVAL 1 MONTH)
GROUP BY m.month_start, c.customer_id, c.name
ORDER BY c.name, m.month_start;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;CROSS JOIN&lt;/code&gt; creates a row for every customer in every month. The &lt;code&gt;LEFT JOIN&lt;/code&gt; then attempts to match orders within each month. When none exist, &lt;code&gt;SUM(o.amount)&lt;/code&gt; returns &lt;code&gt;NULL&lt;/code&gt;, which &lt;code&gt;COALESCE&lt;/code&gt; converts to 0. Without &lt;code&gt;LEFT JOIN&lt;/code&gt;, months with no orders would be omitted entirely, breaking trend analysis.&lt;/p&gt;




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

&lt;p&gt;&lt;em&gt;INNER JOIN&lt;/em&gt; and &lt;em&gt;LEFT JOIN&lt;/em&gt; serve distinct purposes. &lt;em&gt;INNER JOIN&lt;/em&gt; enforces completeness; it filters out uncertainty. &lt;em&gt;LEFT JOIN&lt;/em&gt; exposes gaps, making missing data visible. Choosing correctly ensures your query reflects the actual question — not just the available data.&lt;/p&gt;

&lt;p&gt;Misapplying either can hide business insights or inflate confidence in data coverage. Use &lt;code&gt;EXPLAIN&lt;/code&gt; to verify execution plans, and always consider whether &lt;code&gt;NULL&lt;/code&gt; outcomes are possible — and meaningful.&lt;/p&gt;

&lt;h2&gt;
  
  
  ❓ Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Can LEFT JOIN return more rows than the left table?
&lt;/h3&gt;

&lt;p&gt;Yes. If multiple rows in the right table match a single left row, &lt;strong&gt;LEFT JOIN&lt;/strong&gt; duplicates the left row for each match. For example, one customer with three orders appears three times. This increases result set cardinality and can affect aggregation unless grouped correctly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is INNER JOIN faster than LEFT JOIN?
&lt;/h3&gt;

&lt;p&gt;Generally, yes. &lt;strong&gt;INNER JOIN&lt;/strong&gt; allows more aggressive optimization, including join reordering and early pruning. But with proper indexing on join columns, the performance gap narrows. Always validate with &lt;code&gt;EXPLAIN&lt;/code&gt; on representative data.&lt;/p&gt;

&lt;h3&gt;
  
  
  What happens if I use WHERE with a NULL check after LEFT JOIN?
&lt;/h3&gt;

&lt;p&gt;Filtering with &lt;code&gt;WHERE o.order_id IS NULL&lt;/code&gt; is the correct way to find unmatched rows from the left table. However, filtering on a non-nullable column like &lt;code&gt;WHERE o.status = 'shipped'&lt;/code&gt; excludes rows where &lt;code&gt;o.status&lt;/code&gt; is &lt;code&gt;NULL&lt;/code&gt; — including all unmatched rows. This negates the &lt;strong&gt;LEFT JOIN&lt;/strong&gt; effect, producing results equivalent to an &lt;strong&gt;INNER JOIN&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  📚 References &amp;amp; Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;MySQL JOIN Syntax documentation — official guide to all join types and execution: &lt;a href="https://dev.mysql.com/doc/refman/8.0/en/join.html" rel="noopener noreferrer"&gt;dev.mysql.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;MySQL EXPLAIN statement — understand how your queries are executed: &lt;a href="https://dev.mysql.com/doc/refman/8.0/en/explain.html" rel="noopener noreferrer"&gt;dev.mysql.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Database normalization and referential integrity — design principles that affect join behavior: &lt;a href="https://dev.mysql.com/doc/refman/8.0/en/optimizing-innodb-foreign-keys.html" rel="noopener noreferrer"&gt;dev.mysql.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>mysql</category>
      <category>database</category>
      <category>sql</category>
    </item>
    <item>
      <title>🐍 VirtualBox vs VMware Python development — which one actually fits your workflow?</title>
      <dc:creator>Python-T Point</dc:creator>
      <pubDate>Fri, 15 May 2026 03:37:29 +0000</pubDate>
      <link>https://dev.to/ptp2308/virtualbox-vs-vmware-python-development-which-one-actually-fits-your-workflow-4k8f</link>
      <guid>https://dev.to/ptp2308/virtualbox-vs-vmware-python-development-which-one-actually-fits-your-workflow-4k8f</guid>
      <description>&lt;p&gt;VirtualBox is ill-suited for professional Python development when VMware Workstation is available. The performance delta, integration depth, and operational reliability aren't marginal—they compound across daily workflows in measurable ways.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;📑 Table of Contents&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⚙️ Performance — Why &lt;em&gt;Speed&lt;/em&gt; Isn't Just CPU&lt;/li&gt;
&lt;li&gt;💾 Disk I/O: Raw vs. Dynamic vs. Preallocated&lt;/li&gt;
&lt;li&gt;🧠 Memory Overhead: Why VMware Uses More — But Wisely&lt;/li&gt;
&lt;li&gt;🤝 Integration — How &lt;em&gt;Seamless&lt;/em&gt; Is Your Workflow?&lt;/li&gt;
&lt;li&gt;📁 Shared Folders: Synced or Served?&lt;/li&gt;
&lt;li&gt;🌐 Network Modes: Host-Only, NAT, Bridged — and Python Implications&lt;/li&gt;
&lt;li&gt;📦 Ecosystem — What &lt;em&gt;Tools&lt;/em&gt; Talk to Your VM?&lt;/li&gt;
&lt;li&gt;🛠 Vagrant: VMware is a Paid Plugin, VirtualBox is Free&lt;/li&gt;
&lt;li&gt;🐳 Docker Inside VM: Nested Virtualization Reality&lt;/li&gt;
&lt;li&gt;💰 Cost and Licensing — Is &lt;em&gt;Free&lt;/em&gt; Actually Cheaper?&lt;/li&gt;
&lt;li&gt;🔐 Security and Updates: Who Patches Faster?&lt;/li&gt;
&lt;li&gt;🟩 Final Thoughts&lt;/li&gt;
&lt;li&gt;❓ Frequently Asked Questions&lt;/li&gt;
&lt;li&gt;Can I run both VirtualBox and VMware on the same machine?&lt;/li&gt;
&lt;li&gt;Does VMware Workstation support Linux hosts?&lt;/li&gt;
&lt;li&gt;Is there a performance difference when using WSL2 instead of a VM for Python?&lt;/li&gt;
&lt;li&gt;📚 References &amp;amp; Further Reading&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ⚙️ Performance — Why &lt;em&gt;Speed&lt;/em&gt; Isn't Just CPU
&lt;/h2&gt;

&lt;p&gt;Python development involves frequent small-file I/O: resolving &lt;code&gt;site-packages&lt;/code&gt;, building C extensions (&lt;code&gt;numpy&lt;/code&gt;, &lt;code&gt;cryptography&lt;/code&gt;, &lt;code&gt;psycopg2&lt;/code&gt;), linting, and test execution. Each operation generates hundreds or thousands of &lt;code&gt;stat()&lt;/code&gt;, &lt;code&gt;openat()&lt;/code&gt;, and &lt;code&gt;read()&lt;/code&gt; syscalls, which must traverse the host-guest boundary.&lt;/p&gt;

&lt;p&gt;VMware Workstation uses &lt;strong&gt;VMware Host-Guest File System (HGFS)&lt;/strong&gt; with kernel-level &lt;strong&gt;file attribute caching&lt;/strong&gt; and &lt;strong&gt;bulk metadata handling&lt;/strong&gt;. Its &lt;strong&gt;vmxnet3&lt;/strong&gt; paravirtualized network adapter and &lt;strong&gt;VMM (Virtual Machine Monitor)&lt;/strong&gt; optimize syscall translation and reduce round-trip overhead. VirtualBox relies on &lt;strong&gt;VirtualBox Shared Folders (VBoxSF)&lt;/strong&gt; over a legacy channel (Main Integration Service), offering no effective syscall caching.&lt;/p&gt;

&lt;p&gt;As a result, &lt;code&gt;pip install -r requirements.txt&lt;/code&gt; in a VirtualBox VM with shared folders typically takes &lt;strong&gt;2–3× longer&lt;/strong&gt; than in VMware, due to unbatched &lt;code&gt;stat()&lt;/code&gt; calls.&lt;/p&gt;

&lt;p&gt;Here's a trace of the I/O pattern during a typical install:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ strace -e trace=openat,stat,pread64 pip install requests 2&amp;gt;&amp;amp;1 | head -10
openat(AT_FDCWD, "/usr/lib/python3.11/site-packages", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3
stat("/usr/lib/python3.11/site-packages/requests", 0x7fffbc2a12c0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/tmp/pip-install-abc123/", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 4
stat("/tmp/pip-install-abc123/requests", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
pread64(3, "requests\nurllib3\nchardet\n", 8192, 0) = 23
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Each &lt;code&gt;stat()&lt;/code&gt; and &lt;code&gt;openat()&lt;/code&gt; crosses the hypervisor layer. VMware caches metadata in kernel space, reducing roundtrips. VirtualBox does not. For a dependency tree with 300+ packages, this results in &lt;strong&gt;O(n²) syscall amplification&lt;/strong&gt; —each unused path check repeats over the same uncached remote paths.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“If your dev VM feels ‘slow’, it’s likely due to 50,000+ &lt;code&gt;stat()&lt;/code&gt; calls pip makes—each crossing a high-latency bridge.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  💾 Disk I/O: Raw vs. Dynamic vs. Preallocated
&lt;/h3&gt;

&lt;p&gt;VMware defaults to &lt;strong&gt;preallocated thin provisioning&lt;/strong&gt; with &lt;strong&gt;hot-spot tracking&lt;/strong&gt; : frequently accessed blocks are cached in host RAM. This reduces latency for package installs and database operations.&lt;/p&gt;

&lt;p&gt;VirtualBox uses &lt;strong&gt;VDI (Virtual Disk Image)&lt;/strong&gt; with basic dynamic allocation. It grows on write, but suffers from &lt;strong&gt;fragmentation&lt;/strong&gt; under write-heavy workloads like database migrations or &lt;code&gt;pip wheel&lt;/code&gt; builds.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;fio&lt;/code&gt; to benchmark sustained sequential reads:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ fio --name=seqread --bs=64k --size=1G --runtime=30 --iodepth=4 --direct=1 --rw=read --time_based
seqread: (g=0): rw=read, bs=(R) 64KiB-64KiB, (W) 64KiB-64KiB, (T) 64KiB-64KiB, ioengine=sync, iodepth=4
fio-3.28
Starting 1 process
seqread: Laying out IO file (1 file / 1024MiB)
Jobs: 1 (f=1): [R] [100.0% done] [98MiB/0kiB/0kiB /s] [1568/0/0 iops] [eta 00m:00s]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Typical results for a Windows 11 host, Ubuntu 22.04 guest:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;VMware Workstation&lt;/strong&gt; : ~110–130 MiB/s
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VirtualBox&lt;/strong&gt; : ~60–80 MiB/s&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The gap widens under &lt;strong&gt;4K random read/write&lt;/strong&gt; loads—common with SQLite, PostgreSQL temporary files, and &lt;code&gt;**pycache**&lt;/code&gt; churn.&lt;/p&gt;

&lt;h3&gt;
  
  
  🧠 Memory Overhead: Why VMware Uses More — But Wisely
&lt;/h3&gt;

&lt;p&gt;VMware consumes ~500MB of host RAM per idle VM, compared to ~300MB for VirtualBox. However, it employs &lt;strong&gt;transparent page sharing (TPS)&lt;/strong&gt; and &lt;strong&gt;memory ballooning&lt;/strong&gt; , which deduplicate identical memory pages across VMs.&lt;/p&gt;

&lt;p&gt;For Python development, this means:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiple Ubuntu 22.04 VMs share base OS pages (glibc, kernel modules, Python interpreter binaries).
&lt;/li&gt;
&lt;li&gt;Boot time for a second VM drops significantly because shared pages are already resident.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;VirtualBox lacks TPS. Each VM pays the full RAM cost for duplicated pages, limiting efficient multi-VM workflows.&lt;/p&gt;




&lt;h2&gt;
  
  
  🤝 Integration — How &lt;em&gt;Seamless&lt;/em&gt; Is Your Workflow?
&lt;/h2&gt;

&lt;p&gt;Development velocity depends on transparent cross-environment interaction: file sync, clipboard flow, network routing, and GUI app interoperability.&lt;/p&gt;

&lt;p&gt;VMware's &lt;strong&gt;Unity Mode&lt;/strong&gt; allows Linux GUI applications (PyCharm, VS Code) to appear directly on the Windows desktop, with proper windowing and scaling. VirtualBox offers &lt;strong&gt;Seamless Mode&lt;/strong&gt; , but it’s unstable under GNOME 40+ and KDE Plasma 5.25+, often breaking after kernel updates or display manager changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  📁 Shared Folders: Synced or Served?
&lt;/h3&gt;

&lt;p&gt;VMware presents shared folders via &lt;strong&gt;HGFS with client-side caching&lt;/strong&gt; :  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;File reads are cached in guest RAM.
&lt;/li&gt;
&lt;li&gt;Writes are batched and flushed asynchronously.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;inotify&lt;/strong&gt; events are delivered reliably and promptly—critical for Django’s &lt;code&gt;runserver -autoreload&lt;/code&gt;, &lt;code&gt;pytest-watch&lt;/code&gt;, or &lt;code&gt;mkdocs serve&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;VirtualBox Shared Folders operate over &lt;strong&gt;SMB&lt;/strong&gt; without default client caching. This causes:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;High-latency file access.
&lt;/li&gt;
&lt;li&gt;Missed or delayed inotify events.
&lt;/li&gt;
&lt;li&gt;Editor freezes in VS Code or Sublime Text when indexing large Python projects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Test inotify responsiveness with this script:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import time
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

class Handler(FileSystemEventHandler):
    def on_modified(self, event):
        print(f"Modified: {event.src_path}")

observer = Observer()
observer.schedule(Handler(), path=".", recursive=True)
observer.start()
try:
    while True:
        time.sleep(1)
except KeyboardInterrupt:
    observer.stop()
observer.join()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Run it in both VMs while saving a file from the host editor. VMware captures every write immediately. VirtualBox often skips events or delays notification by 2–3 seconds.&lt;/p&gt;

&lt;h3&gt;
  
  
  🌐 Network Modes: Host-Only, NAT, Bridged — and Python Implications
&lt;/h3&gt;

&lt;p&gt;For local Python web services (&lt;code&gt;Flask&lt;/code&gt;, &lt;code&gt;Django&lt;/code&gt;, &lt;code&gt;FastAPI&lt;/code&gt;), reliable &lt;strong&gt;host-to-guest connectivity&lt;/strong&gt; and &lt;strong&gt;guest-to-internet access&lt;/strong&gt; are essential.&lt;/p&gt;

&lt;p&gt;Both support:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NAT&lt;/strong&gt; (default): guest can reach internet, host cannot reach guest.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bridged&lt;/strong&gt; : guest gets IP on LAN.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Host-only&lt;/strong&gt; : isolated host-VM network.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But VMware adds &lt;strong&gt;persistent NAT port forwarding rules&lt;/strong&gt; with GUI support. Rules survive reboots and can be named (e.g., &lt;code&gt;flask-dev:5000&lt;/code&gt;). It also provides a &lt;strong&gt;DNS proxy&lt;/strong&gt; (&lt;code&gt;vmware-vmx&lt;/code&gt;) that resolves custom domains like &lt;code&gt;project.vm&lt;/code&gt; or &lt;code&gt;api.vm&lt;/code&gt; without hostfile edits.&lt;/p&gt;

&lt;p&gt;VirtualBox requires manual configuration via &lt;code&gt;VBoxManage&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ VBoxManage modifyvm "python-dev-vm" --natpf1 "guestssh,tcp,,2222,,22"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Output:&lt;br&gt;&lt;br&gt;
"&lt;code&gt;&lt;br&gt;
VBoxManage: error: The machine 'python-dev-vm' is already locked for a session (or being locked or unlocked)  &lt;br&gt;
"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;These rules are lost unless exported as part of a scripted definition and don’t restore cleanly after VM reimport.&lt;/p&gt;

&lt;p&gt;VMware applies NAT rules instantly through the UI.&lt;/p&gt;




&lt;h2&gt;
  
  
  📦 Ecosystem — What &lt;em&gt;Tools&lt;/em&gt; Talk to Your VM?
&lt;/h2&gt;

&lt;p&gt;Your hypervisor choice impacts toolchain compatibility with Vagrant, Docker, CI/CD, and provisioning systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  🛠 Vagrant: VMware is a Paid Plugin, VirtualBox is Free
&lt;/h3&gt;

&lt;p&gt;Vagrant supports both, but the &lt;strong&gt;VMware provider&lt;/strong&gt; requires a one-time $80 plugin (&lt;code&gt;vagrant-vmware-desktop&lt;/code&gt;). VirtualBox is free and auto-detected.&lt;/p&gt;

&lt;p&gt;Still, VMware offers:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Faster &lt;code&gt;vagrant up&lt;/code&gt; due to optimized snapshot and disk handling.
&lt;/li&gt;
&lt;li&gt;Stable &lt;code&gt;nfs&lt;/code&gt; and &lt;code&gt;rsync&lt;/code&gt; synced folder modes.
&lt;/li&gt;
&lt;li&gt;Fewer file permission conflicts on Windows hosts.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/22.04"
  config.vm.synced_folder "./code", "/home/vagrant/code", type: "nfs"
  config.vm.provider "vmware_desktop" do |vmware|
    vmware.vmx["memsize"] = "4096"
    vmware.vmx["numvcpus"] = "2"
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;VirtualBox is limited to &lt;code&gt;vboxsf&lt;/code&gt; or &lt;code&gt;rsync&lt;/code&gt;—both struggle with real-time file event propagation and large sync trees.&lt;/p&gt;

&lt;h3&gt;
  
  
  🐳 Docker Inside VM: Nested Virtualization Reality
&lt;/h3&gt;

&lt;p&gt;Running Docker-in-VM (e.g., &lt;code&gt;docker-compose&lt;/code&gt; with Python services) requires &lt;strong&gt;nested virtualization&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;VMware Workstation 17+&lt;/strong&gt; enables &lt;strong&gt;VT-x/AMD-V passthrough&lt;/strong&gt; by default on supported CPUs.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VirtualBox&lt;/strong&gt; supports it, but fails if the host is itself virtualized (e.g., WSL2, cloud VMs, or nested environments).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Verify nested virtualization:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cat /sys/module/kvm_intel/parameters/nested
Y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If output is &lt;code&gt;Y&lt;/code&gt;, you can run &lt;code&gt;docker&lt;/code&gt; with &lt;code&gt;-platform=linux/amd64&lt;/code&gt; even on ARM hardware (via QEMU emulation). VMware also supports &lt;strong&gt;USB 3.1 pass-through&lt;/strong&gt; , useful for IoT Python projects (e.g., serial devices, hardware tokens, Raspberry Pi emulators).&lt;/p&gt;




&lt;h2&gt;
  
  
  💰 Cost and Licensing — Is &lt;em&gt;Free&lt;/em&gt; Actually Cheaper?
&lt;/h2&gt;

&lt;p&gt;VirtualBox is open-source and free. VMware Workstation Pro costs $199 (one-time) for personal use.&lt;/p&gt;

&lt;p&gt;But "free" incurs opportunity cost.&lt;/p&gt;

&lt;p&gt;Estimate:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;10 minutes/day lost to slow &lt;code&gt;pip install&lt;/code&gt; → ~40 hours/year.
&lt;/li&gt;
&lt;li&gt;5 minutes/day troubleshooting autoreload or sync issues → ~20 hours/year.
&lt;/li&gt;
&lt;li&gt;Additional delays from UI crashes or integration failures.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At $25/hour, that’s &lt;strong&gt;$1,500/year in lost productivity&lt;/strong&gt; —eight times the VMware license cost.&lt;/p&gt;

&lt;p&gt;VMware provides &lt;strong&gt;academic discounts&lt;/strong&gt; and free licenses via the &lt;a href="https://www.vmware.com/support/policies/open-source.html" rel="noopener noreferrer"&gt;VMware Open Source Licensing Program&lt;/a&gt; for active open-source contributors.&lt;/p&gt;

&lt;p&gt;VirtualBox remains viable only if:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Budget is strictly zero.
&lt;/li&gt;
&lt;li&gt;Host is Linux (where &lt;code&gt;vboxdrv&lt;/code&gt; integration is more stable).
&lt;/li&gt;
&lt;li&gt;GUI app integration or seamless mode isn’t needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For Windows or macOS hosts, VMware delivers a significantly better return on investment.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔐 Security and Updates: Who Patches Faster?
&lt;/h3&gt;

&lt;p&gt;VMware issues security patches within days of CVE disclosure (e.g., &lt;a href="https://www.vmware.com/security/advisories/VMSA-2023-0003.html" rel="noopener noreferrer"&gt;CVE-2023-20889&lt;/a&gt;). Updates are tested and delivered via built-in auto-updater.&lt;/p&gt;

&lt;p&gt;VirtualBox patch cycles are slower. The &lt;code&gt;vboxdrv&lt;/code&gt; kernel module frequently breaks after Linux kernel updates, requiring manual rebuilds.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo /sbin/vboxconfig
vboxdrv.sh: Starting VirtualBox services.
vboxdrv.sh: Building VirtualBox kernel modules.
vboxdrv.sh: failed: modprobe vboxdrv failed. Please use 'dmesg' to find out why.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Output from &lt;code&gt;dmesg&lt;/code&gt;:&lt;br&gt;&lt;br&gt;
"&lt;code&gt;&lt;br&gt;
vboxdrv: Unknown symbol __stack_chk_guard (err -2)  &lt;br&gt;
vboxdrv: disagrees about version of symbol module_layout  &lt;br&gt;
"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This halts development until resolved—often requiring manual DKMS rebuilds or downgrading the kernel.&lt;/p&gt;




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

&lt;p&gt;The &lt;strong&gt;virtualbox vs vmware python development&lt;/strong&gt; decision shouldn’t hinge on initial price. It should reflect the cumulative cost of I/O latency, integration gaps, and toolchain friction.&lt;/p&gt;

&lt;p&gt;VMware Workstation delivers a &lt;strong&gt;predictable&lt;/strong&gt; , &lt;strong&gt;responsive&lt;/strong&gt; , and &lt;strong&gt;deeply integrated&lt;/strong&gt; environment for Python developers, especially on Windows and macOS. The efficiency gains—faster installs, reliable file watching, stable networking—compound daily.&lt;/p&gt;

&lt;p&gt;VirtualBox is adequate for lightweight use or Linux hosts. But for sustained, high-velocity Python development, VMware is the right default.&lt;/p&gt;

&lt;p&gt;Choose based on time saved, not dollars spent.&lt;/p&gt;

&lt;h2&gt;
  
  
  ❓ Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Can I run both VirtualBox and VMware on the same machine?
&lt;/h3&gt;

&lt;p&gt;Yes, but not simultaneously. Both require exclusive access to hardware virtualization (VT-x/AMD-V). Running one while the other’s kernel modules are loaded can cause system instability or boot failures. Unload one before starting the other. (Also read: &lt;a href="https://pythontpoint.in/python-pip-vs-pipenv-vs-poetry-which-one-should-you/" rel="noopener noreferrer"&gt;🐍 python pip vs pipenv vs poetry — which one should you actually use?&lt;/a&gt;)&lt;/p&gt;

&lt;h3&gt;
  
  
  Does VMware Workstation support Linux hosts?
&lt;/h3&gt;

&lt;p&gt;Yes. VMware Workstation Pro runs on Ubuntu, RHEL, and other major distributions. It integrates well with GNOME and KDE, and supports Wayland (on newer versions). However, many Linux users prefer VirtualBox due to licensing and kernel module transparency.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is there a performance difference when using WSL2 instead of a VM for Python?
&lt;/h3&gt;

&lt;p&gt;Yes — WSL2 outperforms both VMs for most CLI-based Python tasks because it runs a real Linux kernel without full hardware emulation. However, it lacks native GUI app support and has distinct networking behavior. Use WSL2 for terminal-centric workflows; use VMware for full desktop Linux environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  📚 References &amp;amp; Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;VMware Workstation documentation — official guide to features, networking, and performance tuning: &lt;a href="https://docs.vmware.com/en/VMware-Workstation-Pro/index.html" rel="noopener noreferrer"&gt;docs.vmware.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>tutorial</category>
      <category>cloud</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>🚨 S3 Ransomware Response — What to Do in the First Critical Minutes</title>
      <dc:creator>Python-T Point</dc:creator>
      <pubDate>Thu, 14 May 2026 05:24:23 +0000</pubDate>
      <link>https://dev.to/ptp2308/s3-ransomware-response-what-to-do-in-the-first-critical-minutes-5480</link>
      <guid>https://dev.to/ptp2308/s3-ransomware-response-what-to-do-in-the-first-critical-minutes-5480</guid>
      <description>&lt;p&gt;An attacker encrypts every object in your production S3 bucket and replaces them with ransom notes. The next 15 minutes determine whether you restore data in under an hour or face a six-figure payout. This is &lt;strong&gt;S3 ransomware response&lt;/strong&gt; — a high-stakes race where speed, precision, and preparation decide the outcome.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;📑 Table of Contents&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⏱ Minute 0-2 — Stop the Bleed&lt;/li&gt;
&lt;li&gt;🛡 Minute 2-10 — Contain and Assess&lt;/li&gt;
&lt;li&gt;🔀 Minute 10-X — Recovery Decision Tree&lt;/li&gt;
&lt;li&gt;🔐 Preventive Controls — Stop This From Happening Again&lt;/li&gt;
&lt;li&gt;🟩 Final Thoughts&lt;/li&gt;
&lt;li&gt;❓ Frequently Asked Questions&lt;/li&gt;
&lt;li&gt;Can AWS help recover data after an S3 ransomware attack?&lt;/li&gt;
&lt;li&gt;Does S3 Server-Side Encryption (SSE) protect against ransomware?&lt;/li&gt;
&lt;li&gt;How can I test my S3 ransomware recovery plan?&lt;/li&gt;
&lt;li&gt;📚 References &amp;amp; Further Reading&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ⏱ Minute 0-2 — Stop the Bleed
&lt;/h2&gt;

&lt;p&gt;The first two minutes must halt active damage. The objective is to disable write operations before further encryption or data exfiltration occurs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not pay the ransom.&lt;/strong&gt; Payment does not guarantee decryption and increases the likelihood of repeat targeting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not delete the compromised IAM user or role.&lt;/strong&gt; Deletion erases critical audit metadata. Preserve identities for forensic validation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not click links in ransom notes.&lt;/strong&gt; URLs may execute malicious payloads or signal attacker command-and-control infrastructure.&lt;/p&gt;

&lt;p&gt;Immediately block write access to the affected bucket using a deny-all-writes bucket policy:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ aws s3api put-bucket-policy \
    --bucket prod-backups-2024 \
    --policy file://deny-all-writes.json


{
    "ResponseMetadata": {
        "HTTPStatusCode": 204
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This policy denies &lt;code&gt;s3:PutObject&lt;/code&gt;, &lt;code&gt;s3:DeleteObject&lt;/code&gt;, and &lt;code&gt;s3:RestoreObject&lt;/code&gt; across all principals. The &lt;code&gt;Deny&lt;/code&gt; effect overrides any &lt;code&gt;Allow&lt;/code&gt; in IAM or resource policies due to AWS’s policy evaluation order — explicit deny wins, even for administrative users.&lt;/p&gt;

&lt;p&gt;Here’s &lt;code&gt;deny-all-writes.json&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyWritesDuringIncident",
      "Effect": "Deny",
      "Principal": "*",
      "Action": [
        "s3:PutObject",
        "s3:DeleteObject",
        "s3:RestoreObject"
      ],
      "Resource": [
        "arn:aws:s3:::prod-backups-2024/*"
      ]
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;With versioning enabled, attackers cannot permanently erase data without first deleting the latest version — but they can still overwrite objects in place. Blocking new writes prevents encryption of live versions.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛡 Minute 2-10 — Contain and Assess
&lt;/h2&gt;

&lt;p&gt;Next, isolate the compromised identity and initiate forensic data collection.&lt;/p&gt;

&lt;p&gt;Identify the IAM entity behind the malicious writes using CloudTrail. Filter for high-frequency &lt;code&gt;PutObject&lt;/code&gt; operations on the affected bucket:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ aws cloudtrail lookup-events \
    --lookup-attributes AttributeKey=ResourceName,AttributeValue=prod-backups-2024 \
    --start-time 2024-04-15T10:00:00Z \
    --max-results 30


{
    "Events": [
        {
            "EventName": "PutObject",
            "EventTime": "2024-04-15T10:03:12Z",
            "Username": "backup-agent-role",
            "EventSource": "s3.amazonaws.com",
            "Resources": [
                {
                    "ResourceType": "AWS::S3::Object",
                    "ResourceName": "prod-backups-2024/db-snapshot.enc"
                }
            ],
            "AccessKeyId": "ASIA5X2Y3Z4ABCDE5678"
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Key indicators:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;EventName&lt;/strong&gt; is &lt;code&gt;PutObject&lt;/code&gt; with extensions like &lt;code&gt;.enc&lt;/code&gt;, &lt;code&gt;.crypt&lt;/code&gt;, or random suffixes.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Username&lt;/strong&gt; corresponds to non-human roles, especially those with broad S3 access.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AccessKeyId&lt;/strong&gt; begins with &lt;code&gt;ASIA&lt;/code&gt; — signs of assumed role compromise via exposed session tokens.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Disable the role’s permissions by detaching its policies:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ aws iam detach-role-policy \
    --role-name backup-agent-role \
    --policy-arn arn:aws:iam::123456789012:policy/S3FullAccess


{
    "ResponseMetadata": {
        "HTTPStatusCode": 200
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The role remains but loses active permissions. This is faster and more forensic-safe than deletion.&lt;/p&gt;

&lt;p&gt;If using AWS Organizations, apply a service control policy (SCP) to block all S3 actions for the principal:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "BlockS3WritesForCompromisedAccount",
      "Effect": "Deny",
      "Action": "s3:*",
      "Resource": "*",
      "Condition": {
        "StringLike": {
          "aws:PrincipalArn": "arn:aws:iam::123456789012:role/backup-agent-role"
        }
      }
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;SCP enforcement occurs before IAM policy evaluation — meaning this deny takes precedence, regardless of local allow rules.&lt;/p&gt;

&lt;p&gt;If S3 server access logging is enabled, retrieve logs to trace upload sources:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ aws s3api get-bucket-logging --bucket prod-backups-2024


{
    "LoggingEnabled": {
        "TargetBucket": "s3-access-logs-bucket",
        "TargetPrefix": "prod-backups-2024/"
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Download logs from &lt;code&gt;s3-access-logs-bucket&lt;/code&gt; matching the incident window. Filter for &lt;code&gt;PUT&lt;/code&gt; requests with status &lt;code&gt;200&lt;/code&gt; and non-zero request size — confirming successful object uploads.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Containment isn’t just access revocation — it’s preserving forensic data while eliminating active attack pathways.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🔀 Minute 10-X — Recovery Decision Tree
&lt;/h2&gt;

&lt;p&gt;Choose the recovery path based on bucket configuration and backup availability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If versioning is enabled and MFA Delete is disabled:&lt;/strong&gt; Roll back to the last known clean version.&lt;/p&gt;

&lt;p&gt;List versions for affected objects:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ aws s3api list-object-versions \
    --bucket prod-backups-2024 \
    --prefix db-snapshot.sql


{
    "Versions": [
        {
            "Key": "db-snapshot.sql",
            "VersionId": "ExmPLx.idK9BH4iC.EO8LdyX.aI0.PT",
            "IsLatest": true,
            "LastModified": "2024-04-15T10:05:00Z",
            "Size": 20971520
        },
        {
            "Key": "db-snapshot.sql",
            "VersionId": "L45.bXeQ8.jwMpaLshUOwieqz_vwzCw",
            "IsLatest": false,
            "LastModified": "2024-04-15T09:00:00Z",
            "Size": 20971520
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Recover the prior version:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ aws s3api copy-object \
    --bucket prod-backups-2024 \
    --copy-source prod-backups-2024/db-snapshot.sql?versionId=L45.bXeQ8.jwMpaLshUOwieqz_vwzCw \
    --key db-snapshot.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;If versioning is disabled but S3 Object Lock is active in Governance mode:&lt;/strong&gt; You can delete the encrypted object if you have &lt;code&gt;s3:BypassGovernanceRetention&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ aws s3api delete-object \
    --bucket prod-backups-2024 \
    --key db-snapshot.sql \
    --version-id ExmPLx.idK9BH4iC.EO8LdyX.aI0.PT \
    --bypass-governance-retention
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;After deletion, restore from an external backup source.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If Cross-Region Replication (CRR) is configured:&lt;/strong&gt; Check the target bucket in the secondary region:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ aws s3api list-objects-v2 \
    --bucket prod-backups-2024-euwest1 \
    --prefix db-snapshot.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If objects exist, copy them back:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ aws s3 cp s3://prod-backups-2024-euwest1/db-snapshot.sql s3://prod-backups-2024/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;If no versioning or replication, but backups exist elsewhere (e.g., Glacier, EBS snapshots, third-party systems):&lt;/strong&gt; Initiate restore workflows. Do not attempt re-upload until data is verified and staging is ready.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If none of the above apply:&lt;/strong&gt; Recovery is not possible from AWS storage layers. Open a &lt;strong&gt;Priority Support Case&lt;/strong&gt; with AWS. Request forensic support and preservation of CloudTrail logs. Concurrently assess regulatory reporting requirements. Do &lt;strong&gt;not&lt;/strong&gt; engage with attackers.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔐 Preventive Controls — Stop This From Happening Again
&lt;/h2&gt;

&lt;p&gt;Prevention relies on immutable backups, strict least-privilege policies, and automated guardrails.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Enable S3 Versioning on all production buckets&lt;/strong&gt; — enables rollback to pre-attack state. This is the minimum viable recovery mechanism.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enable MFA Delete for critical buckets&lt;/strong&gt; — requires multi-factor authentication to delete or suspend versioning, blocking automated destruction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apply S3 Block Public Access at the account level&lt;/strong&gt; — prevents public exposure that attackers scan for and exploit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use S3 Object Lock in Compliance mode for regulated data&lt;/strong&gt; — prevents deletion or modification even by root users until retention expires.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Restrict S3 write access using&lt;code&gt;aws:SourceArn&lt;/code&gt; and &lt;code&gt;aws:SourceVpc&lt;/code&gt; conditions&lt;/strong&gt; — binds PutObject to specific services or VPCs, reducing risk from compromised credentials.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example: limit PutObject to requests originating from a specific VPC:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "Effect": "Allow",
  "Action": "s3:PutObject",
  "Resource": "arn:aws:s3:::prod-backups-2024/*",
  "Condition": {
    "ArnEquals": {
      "aws:SourceVpc": "vpc-1a2b3c4d"
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This uses the request’s network context during policy evaluation — a stronger control than identity alone.&lt;/p&gt;

&lt;p&gt;Enable S3 access logging and CloudTrail with log file integrity validation. These logs are append-only and signed, making them admissible for post-incident review.&lt;/p&gt;

&lt;p&gt;Monitor configuration drift using AWS Config:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ aws config list-discovered-resources --resource-type AWS::S3::Bucket
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Define custom rules to flag buckets missing versioning, public access, or encryption at rest.&lt;/p&gt;

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

&lt;p&gt;S3 ransomware response is defined by pre-incident configuration. Recovery speed depends on whether versioning was enabled, whether Object Lock was set, and whether least-privilege policies were enforced.&lt;/p&gt;

&lt;p&gt;No operational tooling or debugging skill compensates for missing backups or permissive policies. Your infrastructure as code — Terraform, CloudFormation, CI/CD pipelines — is the frontline of resilience.&lt;/p&gt;

&lt;p&gt;When an attack occurs, the system responds to what was built, not what was intended. The recovery window starts long before the first encrypted object appears.&lt;/p&gt;

&lt;p&gt;Prepare for the attack that bypasses assumptions. Build systems that survive the playbook’s failure.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❓ Frequently Asked Questions
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Can AWS help recover data after an S3 ransomware attack?
&lt;/h3&gt;

&lt;p&gt;AWS can assist with forensic analysis and account recovery through AWS Support, but they cannot decrypt files or restore data unless it’s available in versioned, replicated, or backed-up states. Recovery relies on your configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does S3 Server-Side Encryption (SSE) protect against ransomware?
&lt;/h3&gt;

&lt;p&gt;No. SSE encrypts data at rest, but attackers with write access can still overwrite objects with their own encrypted content. Encryption protects confidentiality, not integrity or availability.&lt;/p&gt;

&lt;h3&gt;
  
  
  How can I test my S3 ransomware recovery plan?
&lt;/h3&gt;

&lt;p&gt;Run controlled chaos engineering drills: simulate an attack by encrypting a test object, then execute your playbook. Verify version restore, policy rollbacks, and communication workflows. Test quarterly.&lt;/p&gt;

&lt;h2&gt;
  
  
  📚 References &amp;amp; Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Amazon S3 Versioning documentation — how to enable and manage object versions: &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/versioning-workflows.html" rel="noopener noreferrer"&gt;docs.aws.amazon.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;AWS IAM Policy Evaluation Logic — deep dive into how Deny, Allow, and conditions are processed: &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_evaluation-logic.html" rel="noopener noreferrer"&gt;docs.aws.amazon.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Amazon S3 Object Lock guide — enforce write-once-read-many (WORM) compliance: &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lock.html" rel="noopener noreferrer"&gt;docs.aws.amazon.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>googlecloud</category>
      <category>cloud</category>
      <category>devops</category>
    </item>
    <item>
      <title>🐍 python pip vs pipenv vs poetry — which one should you actually use?</title>
      <dc:creator>Python-T Point</dc:creator>
      <pubDate>Thu, 14 May 2026 03:37:13 +0000</pubDate>
      <link>https://dev.to/ptp2308/python-pip-vs-pipenv-vs-poetry-which-one-should-you-actually-use-8de</link>
      <guid>https://dev.to/ptp2308/python-pip-vs-pipenv-vs-poetry-which-one-should-you-actually-use-8de</guid>
      <description>&lt;p&gt;Pip is sufficient for most Python projects — you likely don’t need Pipenv or Poetry.&lt;br&gt;&lt;br&gt;
For small-to-medium teams building internal tools, APIs, or data scripts, the added complexity of alternative tools rarely pays off.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;📑 Table of Contents&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📦 pip — The &lt;em&gt;Baseline&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;🔐 pipenv — Bridging &lt;em&gt;Simplicity&lt;/em&gt; and Control&lt;/li&gt;
&lt;li&gt;🧩 How Pipenv Resolves Dependencies&lt;/li&gt;
&lt;li&gt;⚠️ Gotcha: Mixed Environment Behavior&lt;/li&gt;
&lt;li&gt;🐍 Poetry — The &lt;em&gt;Modern&lt;/em&gt; Standard&lt;/li&gt;
&lt;li&gt;⚡ Why Poetry’s Resolver Is Faster&lt;/li&gt;
&lt;li&gt;📦 Publishing Made Predictable&lt;/li&gt;
&lt;li&gt;🧠 Decision Framework — Which Tool for Which &lt;em&gt;Project&lt;/em&gt;?&lt;/li&gt;
&lt;li&gt;🟢 Use pip if:&lt;/li&gt;
&lt;li&gt;🟡 Use Pipenv if:&lt;/li&gt;
&lt;li&gt;🟢 Use Poetry if:&lt;/li&gt;
&lt;li&gt;📊 Comparison Table&lt;/li&gt;
&lt;li&gt;🟩 Final Thoughts&lt;/li&gt;
&lt;li&gt;❓ Frequently Asked Questions&lt;/li&gt;
&lt;li&gt;Can I migrate from Pipenv to Poetry?&lt;/li&gt;
&lt;li&gt;Does pip support lock files now?&lt;/li&gt;
&lt;li&gt;Is Poetry safe for production?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📦 pip — The &lt;em&gt;Baseline&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;pip&lt;/code&gt; installs Python packages from PyPI into the active environment. That’s it.&lt;/p&gt;

&lt;p&gt;Running &lt;code&gt;pip install requests&lt;/code&gt; triggers this sequence:&lt;br&gt;&lt;br&gt;
1. Resolve &lt;code&gt;requests&lt;/code&gt; to a distribution (wheel or sdist) from the index (default: &lt;a href="https://pypi.org" rel="noopener noreferrer"&gt;https://pypi.org&lt;/a&gt;).&lt;br&gt;&lt;br&gt;
2. Download the artifact, verify its hash if available, and extract it.&lt;br&gt;&lt;br&gt;
3. Execute the build backend (&lt;code&gt;setuptools&lt;/code&gt;, &lt;code&gt;poetry-core&lt;/code&gt;, etc.) specified in &lt;code&gt;pyproject.toml&lt;/code&gt; or &lt;code&gt;setup.py&lt;/code&gt; to generate metadata.&lt;br&gt;&lt;br&gt;
4. Copy files into &lt;code&gt;site-packages/&lt;/code&gt; and populate &lt;code&gt;.dist-info&lt;/code&gt; directories with dependency records.&lt;/p&gt;

&lt;p&gt;This process works, but &lt;code&gt;pip&lt;/code&gt; has no native concept of direct vs. transitive dependencies. That’s what &lt;code&gt;requirements.txt&lt;/code&gt; addresses — as a snapshot mechanism.&lt;/p&gt;

&lt;p&gt;Freeze dependencies:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ pip freeze &amp;gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Expected output: none (file created silently).&lt;/p&gt;

&lt;p&gt;Resulting content:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;requests==2.32.0
urllib3==2.2.3
certifi==2024.8.30
charset-normalizer==3.4.0
idna==3.7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;But this includes every installed package — direct and indirect — with no distinction. Worse, without tight version pins, dependency resolution can vary across installations because &lt;code&gt;pip&lt;/code&gt; does not lock the full dependency tree by default.&lt;/p&gt;

&lt;p&gt;Despite this, for targeted use cases — Docker builds, CI pipelines, standalone scripts — it’s effective. Pin versions strictly, commit &lt;code&gt;requirements.txt&lt;/code&gt;, and you have reproducible installs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"If your team enforces version discipline, pip alone is production-grade."&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🔐 pipenv — Bridging &lt;em&gt;Simplicity&lt;/em&gt; and Control
&lt;/h2&gt;

&lt;p&gt;Pipenv integrates &lt;code&gt;pip&lt;/code&gt; and &lt;code&gt;virtualenv&lt;/code&gt;, adds dependency locking, and uses two files:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Pipfile&lt;/code&gt; — TOML format listing direct and dev dependencies.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Pipfile.lock&lt;/code&gt; — JSON snapshot of the full resolved tree, including hashes and sources.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]
requests = "*"
flask = "==2.3.3"

[dev-packages]
pytest = "*"

[requires]
python_version = "3.12"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Running &lt;code&gt;pipenv install&lt;/code&gt; generates &lt;code&gt;Pipfile.lock&lt;/code&gt;, which records the exact SHA256 hash of each downloaded package. This ensures byte-for-byte identical installs across machines — critical for security auditing and reproducibility.&lt;/p&gt;

&lt;p&gt;Under the hood, Pipenv uses &lt;code&gt;pip&lt;/code&gt; but wraps it with a custom dependency resolver based on &lt;code&gt;pip-tools&lt;/code&gt;. It also manages per-project virtual environments automatically, so there’s no need to manually activate them.&lt;/p&gt;

&lt;p&gt;Install a package:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ pipenv install requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Output:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Creating a virtualenv for this project...
Pipfile: /code/Pipfile
Using /usr/bin/python3.12 (3.12.6) to create virtualenv
...
✔ Installation Succeeded
Pipfile.lock (abc123) out of date, updating...
Locking [dev-packages] dependencies...
Locking [packages] dependencies...
✔ Success!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Run code in context:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ pipenv run python -c "import requests; print(requests.__version__)"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Output:&lt;/p&gt;

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

&lt;/div&gt;

&lt;p&gt;The catch: Pipenv has seen no meaningful updates since 2022. Its resolver is slower than modern alternatives, and complex dependency graphs — especially those with environment markers or conditional extras — can trigger long resolution times or failures.&lt;/p&gt;

&lt;p&gt;So while &lt;strong&gt;python pip vs pipenv vs poetry&lt;/strong&gt; positions Pipenv as a middle ground, it’s now effectively legacy. It remains usable for existing projects, but not recommended for new ones.&lt;/p&gt;

&lt;h3&gt;
  
  
  🧩 How Pipenv Resolves Dependencies
&lt;/h3&gt;

&lt;p&gt;Pipenv uses a backtracking resolver that tests combinations of versions until a valid set is found. Dependency resolution in this model is &lt;strong&gt;NP-hard&lt;/strong&gt; , meaning worst-case performance scales exponentially with the number of interdependent packages.&lt;/p&gt;

&lt;p&gt;For instance, if &lt;code&gt;A&lt;/code&gt; requires &lt;code&gt;B&amp;gt;=1.0,&amp;lt;3.0&lt;/code&gt; and &lt;code&gt;C==2.1&lt;/code&gt;, but &lt;code&gt;C==2.1&lt;/code&gt; requires &lt;code&gt;B==1.5&lt;/code&gt;, the resolver must backtrack after selecting incompatible versions like &lt;code&gt;B==2.0&lt;/code&gt;. As a result, large projects can take over 30 seconds to resolve. In contrast, &lt;code&gt;pip&lt;/code&gt; with &lt;code&gt;--use-feature=2020-resolver&lt;/code&gt; uses a more efficient backtracking algorithm with early conflict detection, reducing resolution time significantly. &lt;/p&gt;

&lt;h3&gt;
  
  
  ⚠️ Gotcha: Mixed Environment Behavior
&lt;/h3&gt;

&lt;p&gt;Using &lt;code&gt;pip install&lt;/code&gt; inside a Pipenv-managed project bypasses &lt;code&gt;Pipfile.lock&lt;/code&gt;. The lock file won’t reflect those changes, leading to environment drift.&lt;/p&gt;

&lt;p&gt;Always use &lt;code&gt;pipenv install&lt;/code&gt;. Never call &lt;code&gt;pip&lt;/code&gt; directly in such projects.&lt;/p&gt;




&lt;h2&gt;
  
  
  🐍 Poetry — The &lt;em&gt;Modern&lt;/em&gt; Standard
&lt;/h2&gt;

&lt;p&gt;Poetry treats every project as a package from the start, using &lt;code&gt;pyproject.toml&lt;/code&gt; as the single source of truth.&lt;/p&gt;

&lt;p&gt;Unlike Pipenv, Poetry aligns with &lt;a href="https://peps.python.org/pep-0621" rel="noopener noreferrer"&gt;PEP 621&lt;/a&gt;, enabling interoperability with standard tooling like &lt;code&gt;build&lt;/code&gt; and &lt;code&gt;twine&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A minimal &lt;code&gt;pyproject.toml&lt;/code&gt; defines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Project metadata (name, version, authors)
&lt;/li&gt;
&lt;li&gt;Dependencies (&lt;code&gt;dependencies&lt;/code&gt;, &lt;code&gt;group.dev.dependencies&lt;/code&gt;)
&lt;/li&gt;
&lt;li&gt;Build system requirements&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

[project]
name = "my-api"
version = "0.1.0"
dependencies = [
    "flask&amp;gt;=2.3.0",
    "requests[socks]",
]

[project.optional-dependencies]
dev = [
    "pytest",
    "black",
]

[tool.poetry]
# Legacy section (still supported)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Running &lt;code&gt;poetry install&lt;/code&gt; triggers:&lt;br&gt;&lt;br&gt;
1. Read &lt;code&gt;pyproject.toml&lt;/code&gt; and resolve dependencies using Poetry’s &lt;strong&gt;custom SAT-based solver&lt;/strong&gt; (&lt;code&gt;python-poetry/poetry-core&lt;/code&gt;).&lt;br&gt;&lt;br&gt;
2. Generate &lt;code&gt;poetry.lock&lt;/code&gt; — a deterministic snapshot containing versions, hashes, and full dependency tree.&lt;br&gt;&lt;br&gt;
3. Create or reuse an isolated virtual environment.&lt;br&gt;&lt;br&gt;
4. Install the local project in editable mode by default.&lt;/p&gt;

&lt;p&gt;Lock file excerpt:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[[package]]
name = "requests"
version = "2.32.0"
dependencies = {
    certifi = "&amp;gt;=2017.4.17",
    charset-normalizer = "&amp;gt;=2,&amp;lt;5",
    idna = "&amp;gt;=2.5,&amp;lt;4",
    urllib3 = "&amp;gt;=1.21.1,&amp;lt;3"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This format is more readable than Pipenv’s JSON and supports advanced features:&lt;br&gt;&lt;br&gt;
- Multiple index sources (e.g., private PyPI, Git URLs)&lt;br&gt;&lt;br&gt;
- Optional groups (&lt;code&gt;poetry install --with dev&lt;/code&gt;)&lt;br&gt;&lt;br&gt;
- Local path dependencies (&lt;code&gt;../shared-utils&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;Add a dependency:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ poetry add pandas
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Output:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Using version ^2.2.3 for pandas
Updating dependencies
Resolving dependencies... (0.8s)
Writing lock file
Package operations: 14 installs, 0 updates, 0 removals
  • Installing numpy (1.26.4)
  • Installing pandas (2.2.3)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Run code:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ poetry run python -c "import pandas; print(pandas.__version__)"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Output:&lt;/p&gt;

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

&lt;/div&gt;

&lt;p&gt;Publishing is built in:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ poetry publish
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This builds a wheel and sdist, then uploads to PyPI or a private registry — all from one configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  ⚡ Why Poetry’s Resolver Is Faster
&lt;/h3&gt;

&lt;p&gt;Poetry uses a &lt;strong&gt;SAT (Boolean satisfiability) solver&lt;/strong&gt; adapted for dependency constraints. It translates requirements into logical clauses:&lt;br&gt;&lt;br&gt;
- &lt;code&gt;A depends on B&amp;gt;=1.0&lt;/code&gt; becomes &lt;code&gt;(B=1.0 ∨ B=1.1 ∨ ... ∨ B=2.9)&lt;/code&gt;&lt;br&gt;&lt;br&gt;
- &lt;code&gt;C requires B==1.5&lt;/code&gt; becomes &lt;code&gt;(B=1.5)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It then applies unit propagation and conflict-driven clause learning (CDCL) to eliminate invalid paths early — techniques also used in hardware verification and modern constraint solvers.&lt;/p&gt;

&lt;p&gt;This approach scales significantly better than naive backtracking, especially for large or tightly constrained dependency graphs.&lt;/p&gt;

&lt;h3&gt;
  
  
  📦 Publishing Made Predictable
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;poetry build&lt;/code&gt; produces a clean wheel containing only what’s declared in &lt;code&gt;pyproject.toml&lt;/code&gt;. There’s no reliance on &lt;code&gt;MANIFEST.in&lt;/code&gt;, reducing the risk of including unintended files like &lt;code&gt;.pyc&lt;/code&gt; or test directories.&lt;/p&gt;

&lt;p&gt;This contrasts with legacy &lt;code&gt;setup.py&lt;/code&gt; workflows, where accidental inclusions are common and hard to audit.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 Decision Framework — Which Tool for Which &lt;em&gt;Project&lt;/em&gt;?
&lt;/h2&gt;

&lt;p&gt;Choose based on &lt;strong&gt;project scope&lt;/strong&gt; , &lt;strong&gt;team size&lt;/strong&gt; , and &lt;strong&gt;delivery method&lt;/strong&gt; , not trends.&lt;/p&gt;

&lt;h3&gt;
  
  
  🟢 Use pip if:
&lt;/h3&gt;

&lt;p&gt;- Writing scripts, notebooks, or throwaway prototypes.&lt;br&gt;&lt;br&gt;
- Deploying via Docker, where &lt;code&gt;requirements.txt&lt;/code&gt; is sufficient.&lt;br&gt;&lt;br&gt;
- Working in a small, disciplined team that pins versions strictly.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM python:3.12-slim
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "app.py"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here, &lt;strong&gt;python pip vs pipenv vs poetry&lt;/strong&gt; favors pip — minimal layers, maximum control.&lt;/p&gt;

&lt;h3&gt;
  
  
  🟡 Use Pipenv if:
&lt;/h3&gt;

&lt;p&gt;- Maintaining an existing project that already uses it.&lt;br&gt;&lt;br&gt;
- Wanting automatic virtual environments without adopting Poetry.&lt;br&gt;&lt;br&gt;
- Needing lock files but not planning to publish packages.&lt;/p&gt;

&lt;p&gt;Do not start new projects with Pipenv. The ecosystem has moved on.&lt;/p&gt;

&lt;h3&gt;
  
  
  🟢 Use Poetry if:
&lt;/h3&gt;

&lt;p&gt;- Building a reusable library or long-lived service.&lt;br&gt;&lt;br&gt;
- Working on a team requiring strict reproducibility.&lt;br&gt;&lt;br&gt;
- Publishing to PyPI or a private index.&lt;br&gt;&lt;br&gt;
- Needing dependency groups (&lt;code&gt;dev&lt;/code&gt;, &lt;code&gt;test&lt;/code&gt;, &lt;code&gt;docs&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Poetry excels when code is treated as a product, not a script.&lt;/p&gt;

&lt;h3&gt;
  
  
  📊 Comparison Table
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lock file?&lt;/strong&gt; pip: only via &lt;code&gt;freeze&lt;/code&gt;; Pipenv: yes; Poetry: yes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Virtual env management?&lt;/strong&gt; pip: no; Pipenv: yes; Poetry: yes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standard config?&lt;/strong&gt; pip: no; Pipenv: no; Poetry: yes (&lt;code&gt;pyproject.toml&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependency groups?&lt;/strong&gt; pip: manual; Pipenv: yes; Poetry: yes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Package publishing?&lt;/strong&gt; pip: partial; Pipenv: no; Poetry: full&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In short:&lt;br&gt;&lt;br&gt;
- &lt;strong&gt;pip&lt;/strong&gt; for simplicity.&lt;br&gt;&lt;br&gt;
- &lt;strong&gt;Poetry&lt;/strong&gt; for rigor.&lt;br&gt;&lt;br&gt;
- &lt;strong&gt;Pipenv&lt;/strong&gt; — only if already committed.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Treat dependency tools like databases: choose for consistency, not convenience."&lt;/p&gt;
&lt;/blockquote&gt;




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

&lt;p&gt;Dependency management exists to eliminate surprises. Whether you use &lt;code&gt;pip freeze&lt;/code&gt; or &lt;code&gt;poetry lock&lt;/code&gt;, the goal is the same: ensure identical environments from dev to production.&lt;/p&gt;

&lt;p&gt;The adoption of &lt;code&gt;pyproject.toml&lt;/code&gt; as a standard has made Poetry the de facto choice for new, serious Python projects. It’s not the only viable option, but it’s the one actively advancing the ecosystem — with faster resolution, reliable builds, and broad tool compatibility.&lt;/p&gt;

&lt;p&gt;Meanwhile, &lt;code&gt;pip&lt;/code&gt; remains fully valid for containerized apps and scripts. Don’t add layers unless the project demands them.&lt;/p&gt;

&lt;p&gt;Ultimately, &lt;strong&gt;python pip vs pipenv vs poetry&lt;/strong&gt; isn’t about superiority — it’s about fit. A startup MVP doesn’t require the same rigor as a financial system. Match the tool to the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  ❓ Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Can I migrate from Pipenv to Poetry?
&lt;/h3&gt;

&lt;p&gt;Yes. Run &lt;code&gt;pipenv requirements --hash &amp;gt; requirements.txt&lt;/code&gt;, then &lt;code&gt;poetry init&lt;/code&gt; and import dependencies manually. Or use tools like &lt;code&gt;pip2poetry&lt;/code&gt; for automation. (Also read: &lt;a href="https://pythontpoint.in/terraform-vs-pulumi-which-to-choose-for-iac-in-2024/" rel="noopener noreferrer"&gt;☁️ Terraform vs Pulumi — Which to Choose for IaC&lt;/a&gt;)&lt;/p&gt;

&lt;h3&gt;
  
  
  Does pip support lock files now?
&lt;/h3&gt;

&lt;p&gt;Not natively. &lt;code&gt;pip freeze &amp;gt; requirements.txt&lt;/code&gt; creates snapshots, but lacks dependency tree metadata. Tools like &lt;code&gt;pip-tools&lt;/code&gt; provide proper locking via &lt;code&gt;pip-compile&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is Poetry safe for production?
&lt;/h3&gt;

&lt;p&gt;Yes. It’s used in production at large organizations. The lock file is deterministic and hash-verified, meeting compliance and audit requirements.&lt;/p&gt;

</description>
      <category>python</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>💻 How to vm migrate from vmware to kvm — key tips and pitfalls</title>
      <dc:creator>Python-T Point</dc:creator>
      <pubDate>Wed, 13 May 2026 03:38:15 +0000</pubDate>
      <link>https://dev.to/ptp2308/how-to-vm-migrate-from-vmware-to-kvm-key-tips-and-pitfalls-522c</link>
      <guid>https://dev.to/ptp2308/how-to-vm-migrate-from-vmware-to-kvm-key-tips-and-pitfalls-522c</guid>
      <description>&lt;p&gt;Two virtual machines, identical in configuration and OS, migrated from VMware to KVM using different tools: one completes in 22 minutes with full network functionality; the other fails after 45 minutes with a kernel panic. Same hypervisor destination. Same source vCenter. Same guest OS. The difference? Whether &lt;strong&gt;virt-v2v&lt;/strong&gt; was used — or avoided. If you need to &lt;em&gt;vm migrate from vmware to kvm&lt;/em&gt; , this tool isn’t optional. It’s the only method that consistently produces bootable, production-ready KVM guests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;📑 Table of Contents&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🚀 Prerequisites — What You &lt;em&gt;Need&lt;/em&gt; Before Running virt-v2v&lt;/li&gt;
&lt;li&gt;🔐 Access to VMware&lt;/li&gt;
&lt;li&gt;💾 Destination Options&lt;/li&gt;
&lt;li&gt;🧩 Supported Guest OSes&lt;/li&gt;
&lt;li&gt;🔌 Connection — How virt-v2v &lt;em&gt;Talks&lt;/em&gt; to VMware&lt;/li&gt;
&lt;li&gt;💽 Conversion — What Happens During the &lt;em&gt;Transformation&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;🔧 Windows-Specific Changes&lt;/li&gt;
&lt;li&gt;📦 Output Formats&lt;/li&gt;
&lt;li&gt;🚫 Common Conversion Failures&lt;/li&gt;
&lt;li&gt;📤 Deployment — Getting the VM to KVM &lt;em&gt;Efficiently&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;🔗 Network Configuration&lt;/li&gt;
&lt;li&gt;🔁 Post-Migration Checks&lt;/li&gt;
&lt;li&gt;🟩 Final Thoughts&lt;/li&gt;
&lt;li&gt;❓ Frequently Asked Questions&lt;/li&gt;
&lt;li&gt;Can I convert VMs without powering them off?&lt;/li&gt;
&lt;li&gt;Does virt-v2v support encrypted VMware VMs?&lt;/li&gt;
&lt;li&gt;Can I automate migration of multiple VMs?&lt;/li&gt;
&lt;li&gt;📚 References &amp;amp; Further Reading&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🚀 Prerequisites — What You &lt;em&gt;Need&lt;/em&gt; Before Running virt-v2v
&lt;/h2&gt;

&lt;p&gt;virt-v2v is not a standalone binary. It's a pipeline built on libvirt, QEMU, and libguestfs. You must run it from a Linux conversion host capable of connecting to both VMware (via vCenter or ESXi) and the destination KVM environment.&lt;/p&gt;

&lt;p&gt;The host requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;libvirt&lt;/strong&gt; with QEMU/KVM driver
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;virt-v2v&lt;/strong&gt; (part of the &lt;code&gt;virt-v2v&lt;/code&gt; package on most distributions)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;qemu-img&lt;/strong&gt; for intermediate disk handling
&lt;/li&gt;
&lt;li&gt;Network access to vCenter/ESXi and destination KVM host
&lt;/li&gt;
&lt;li&gt;Sufficient scratch space — at least 1.5× the size of the largest VM being converted&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On Red Hat–based systems (RHEL, Rocky Linux, AlmaLinux):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo dnf install virt-v2v libguestfs-tools-c qemu-img
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Installed:
  virt-v2v-1.4.6-1.el9.x86_64
  libguestfs-1:1.48.20-1.el9.x86_64
  qemu-img-6.2.0-30.el9_3.1.x86_64
Complete!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Under the hood, &lt;code&gt;virt-v2v&lt;/code&gt; uses &lt;strong&gt;libguestfs&lt;/strong&gt; to launch a minimal appliance via &lt;code&gt;guestfsd&lt;/code&gt;. This mounts the source VM's filesystem to perform targeted modifications: removing VMware-specific drivers like &lt;code&gt;vmxnet3&lt;/code&gt;, injecting KVM equivalents (&lt;code&gt;virtio_net&lt;/code&gt;, &lt;code&gt;virtio_blk&lt;/code&gt;), and rewriting bootloader configuration. This is not a blind disk copy — it’s a &lt;em&gt;guest-aware transformation&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔐 Access to VMware
&lt;/h3&gt;

&lt;p&gt;virt-v2v uses URIs to connect to VMware:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;vpx://&lt;/strong&gt; — for vCenter-managed clusters
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;esx://&lt;/strong&gt; — for standalone ESXi hosts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’ll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;vCenter or ESXi hostname/IP
&lt;/li&gt;
&lt;li&gt;Username with read-only VM privileges
&lt;/li&gt;
&lt;li&gt;Password (or keyring integration)
&lt;/li&gt;
&lt;li&gt;Source VM name or inventory path&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  💾 Destination Options
&lt;/h3&gt;

&lt;p&gt;Output formats include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Local libvirt storage pool (&lt;code&gt;-o libvirt&lt;/code&gt;)
&lt;/li&gt;
&lt;li&gt;Remote KVM host via SSH (&lt;code&gt;-oo libvirt_uri=qemu+ssh://…&lt;/code&gt;)
&lt;/li&gt;
&lt;li&gt;Raw file output (&lt;code&gt;-o null -os /path/to/output&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The most common production setup uses &lt;code&gt;qemu+ssh&lt;/code&gt; to stream the VM directly to a remote KVM host.&lt;/p&gt;

&lt;h3&gt;
  
  
  🧩 Supported Guest OSes
&lt;/h3&gt;

&lt;p&gt;virt-v2v officially supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RHEL/CentOS 6–9
&lt;/li&gt;
&lt;li&gt;Debian 10–12
&lt;/li&gt;
&lt;li&gt;Ubuntu 18.04–22.04
&lt;/li&gt;
&lt;li&gt;Windows Server 2008–2022 (requires &lt;code&gt;virtio-win&lt;/code&gt; drivers)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unsupported or legacy distributions may boot, but often fail at initramfs or driver loading without manual fixes.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔌 Connection — How virt-v2v &lt;em&gt;Talks&lt;/em&gt; to VMware
&lt;/h2&gt;

&lt;p&gt;virt-v2v connects directly to the VMware vSphere API over HTTPS. No manual OVA export is required.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ virt-v2v -ic vpx://vcenter.example.com/Datacenter/host/Cluster \
  -it vddk -ip esx_password \
  'Windows-VM'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Breakdown:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-ic&lt;/code&gt;: input connection URI
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-it vddk&lt;/code&gt;: enables VMware’s &lt;strong&gt;Virtual Disk Development Kit&lt;/strong&gt; (VDDK)
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-ip&lt;/code&gt;: prompts for password (prefer over plaintext)
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;'Windows-VM'&lt;/code&gt;: VM name as registered in vCenter&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;VDDK enables &lt;strong&gt;hot disk reading&lt;/strong&gt; via VMware’s &lt;strong&gt;VixDiskLib&lt;/strong&gt; , allowing direct access to &lt;code&gt;.vmdk&lt;/code&gt; files on ESXi datastores — even while the VM is running. Without VDDK, virt-v2v falls back to NBD or HTTPS transport, which are 3–5× slower and require the VM to be powered off.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[   0.0] Opening the source -i libvirt -ic vpx://...
[   2.1] Creating an overlay to protect the source from being modified
[   3.5] Opening the overlay
[  10.2] Inspecting the overlay
[  15.0] Checking for sufficient free disk space in the overlay
[  15.1] Converting Windows-VM to run on KVM
[  16.0] Creating output metadata
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;VDDK requires the &lt;strong&gt;VDDK library&lt;/strong&gt; installed on the conversion host. Download from VMware and extract:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ tar -xzf VMware-vix-disklib-*.tar.gz -C /opt
$ virt-v2v ... -oo vddk-libdir=/opt/vmware-vddk/lib64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This library path must point to the &lt;code&gt;lib64&lt;/code&gt; directory containing &lt;code&gt;libvixDiskLib.so&lt;/code&gt;. For production migrations, VDDK is non-negotiable — skipping it increases transfer time and requires downtime.&lt;/p&gt;




&lt;h2&gt;
  
  
  💽 Conversion — What Happens During the &lt;em&gt;Transformation&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;virt-v2v performs a deep guest reconfiguration, not a simple format swap. The process includes:&lt;/p&gt;

&lt;p&gt;1. &lt;strong&gt;Disk download&lt;/strong&gt; via VDDK → temporary qcow2 overlay&lt;br&gt;&lt;br&gt;
2. &lt;strong&gt;Guest inspection&lt;/strong&gt; : reads &lt;code&gt;/etc/os-release&lt;/code&gt;, bootloader, partitioning&lt;br&gt;&lt;br&gt;
3. &lt;strong&gt;Driver substitution&lt;/strong&gt; : replaces &lt;code&gt;vmxnet3&lt;/code&gt; with &lt;code&gt;virtio_net&lt;/code&gt;, &lt;code&gt;pvscsi&lt;/code&gt; with &lt;code&gt;virtio_scsi&lt;/code&gt;&lt;br&gt;&lt;br&gt;
4. &lt;strong&gt;Bootloader update&lt;/strong&gt; : GRUB config rewritten for virtio block devices&lt;br&gt;&lt;br&gt;
5. &lt;strong&gt;Initramfs rebuild&lt;/strong&gt; : &lt;code&gt;dracut&lt;/code&gt; or &lt;code&gt;update-initramfs&lt;/code&gt; regenerates with &lt;code&gt;virtio&lt;/code&gt; modules&lt;br&gt;&lt;br&gt;
6. &lt;strong&gt;Disk export&lt;/strong&gt; : final image pushed to target storage &lt;/p&gt;

&lt;p&gt;For a Linux VM named &lt;code&gt;webserver-01&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ virt-v2v -ic vpx://vcenter.example.com/Datacenter/host/Cluster \
  -oo vddk-libdir=/opt/vmware-vddk/lib64 \
  -o libvirt -os default \
  'webserver-01'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Output:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[  50.2] Creating local storage path for the converted disk
[  51.0] Creating qcow2 disk (for libvirt) with size 21.5G
[  60.3] Setting a random seed for the new guest
[  61.5] Changing the root password
[  65.0] Installing virtio drivers (Linux)
[  68.2] Rewriting GRUB configuration
[  70.1] Updating initramfs
[  75.4] Building the libvirt XML
[  76.0] Creating libvirt domain...
Domain created successfully.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;strong&gt;initramfs rebuild&lt;/strong&gt; is critical. If &lt;code&gt;virtio_blk&lt;/code&gt; is absent during early boot, the kernel cannot detect the root device and will panic with:&lt;br&gt;&lt;br&gt;
&lt;code&gt;"ALERT! /dev/sda1 does not exist. Dropping to a shell."&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;virt-v2v avoids this by &lt;code&gt;chroot&lt;/code&gt;-ing into the guest disk and running:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;dracut -add-drivers virtio_pci,virtio_blk,virtio_net&lt;/code&gt; (RHEL/CentOS)
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;update-initramfs -u&lt;/code&gt; (Debian/Ubuntu)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ensures the initramfs contains the drivers needed before the real root mounts.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;virt-v2v doesn’t just move a VM — it &lt;em&gt;replatforms&lt;/em&gt; it, ensuring kernel, bootloader, and drivers align with KVM’s virtual hardware.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  🔧 Windows-Specific Changes
&lt;/h3&gt;

&lt;p&gt;For Windows VMs, virt-v2v injects &lt;strong&gt;virtio-win&lt;/strong&gt; drivers into the offline registry using &lt;code&gt;guestfs_win_inject_drivers()&lt;/code&gt;. This adds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;viostor&lt;/code&gt; (virtio block)
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;vioscsi&lt;/code&gt; (virtio SCSI)
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;viorng&lt;/code&gt; (entropy)
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;qemu-ga&lt;/code&gt; (optional)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And sets each service &lt;code&gt;Start&lt;/code&gt; value to &lt;code&gt;0&lt;/code&gt; (boot time load) in:&lt;br&gt;&lt;br&gt;
&lt;code&gt;HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;virtio-win.iso&lt;/code&gt; must be accessible:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ virt-v2v ... -oo virtio-win-iso=/home/user/virtio-win.iso 'WinServer-2019'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Without this, Windows fails to detect the boot disk and blue-screens.&lt;/p&gt;

&lt;h3&gt;
  
  
  📦 Output Formats
&lt;/h3&gt;

&lt;p&gt;Default output is &lt;strong&gt;qcow2&lt;/strong&gt; with &lt;strong&gt;sparse allocation&lt;/strong&gt;. To use raw:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ virt-v2v ... -of raw
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Raw is preferred for LVM, iSCSI, or direct device mapping. qcow2 supports snapshots and compression, but adds minor I/O overhead.&lt;/p&gt;

&lt;h3&gt;
  
  
  🚫 Common Conversion Failures
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;" No OS found"&lt;/strong&gt;: guest OS not in supported list, or &lt;code&gt;/etc/os-release&lt;/code&gt; missing/corrupted
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;dracut-initqueue timeout&lt;/strong&gt; : &lt;code&gt;virtio_blk&lt;/code&gt; missing from initramfs (often due to chroot failure in scratch space)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No network post-boot&lt;/strong&gt; : &lt;code&gt;vmxnet3&lt;/code&gt; driver not replaced, or &lt;code&gt;70-persistent-net.rules&lt;/code&gt; locks old MAC &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Validate OS compatibility against the &lt;a href="https://access.redhat.com/articles/virt-v2v-supported-conversions" rel="noopener noreferrer"&gt;official list&lt;/a&gt; before starting.&lt;/p&gt;




&lt;h2&gt;
  
  
  📤 Deployment — Getting the VM to KVM &lt;em&gt;Efficiently&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;After conversion, deploy the VM to KVM. The default &lt;code&gt;-o libvirt&lt;/code&gt; registers it locally. For remote deployment:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ virt-v2v -ic vpx://vcenter.example.com/... \
  -o null -os /var/lib/libvirt/images \
  -oo output_mode=local \
  -oo libvirt_uri=qemu+ssh://kvmhost.example.com/system \
  'webserver-01'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-o null&lt;/code&gt;: skips local libvirt registration
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-os /var/lib/libvirt/images&lt;/code&gt;: writes disk to local scratch
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-oo libvirt_uri=…&lt;/code&gt;: connects to remote &lt;code&gt;libvirtd&lt;/code&gt; over SSH
&lt;/li&gt;
&lt;li&gt;Then uses &lt;code&gt;scp&lt;/code&gt; to transfer disk, &lt;code&gt;virDomainDefineXML()&lt;/code&gt; to define domain &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This avoids double-transfer of large disks — a key efficiency when migrating dozens of VMs.&lt;/p&gt;

&lt;p&gt;On the target KVM host:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ virsh list --all


 Id   Name             State
----------------------------------
 3    webserver-01     running
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Check disk format:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ qemu-img info /var/lib/libvirt/images/webserver-01-sda


image: webserver-01-sda
file format: qcow2
virtual size: 50 GiB
disk size: 14.2 GiB
backing file: (none)
cluster_size: 65536
Format specific details:
    compat: 1.1
    lazy refcounts: false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;disk size&lt;/code&gt; is much smaller than &lt;code&gt;virtual size&lt;/code&gt; due to &lt;strong&gt;sparse allocation&lt;/strong&gt; — the file only consumes space for written blocks.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔗 Network Configuration
&lt;/h3&gt;

&lt;p&gt;virt-v2v preserves NIC count and MAC addresses, but changes interface type from &lt;code&gt;vmxnet3&lt;/code&gt; to &lt;code&gt;virtio&lt;/code&gt;. Ensure the KVM bridge (e.g., &lt;code&gt;br0&lt;/code&gt;) is active and bridged to physical NIC.&lt;/p&gt;

&lt;p&gt;If no IP is assigned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Verify the bridge: &lt;code&gt;ip link show br0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Check libvirt network: &lt;code&gt;virsh net-list&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Confirm firewall allows traffic on bridge interface &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🔁 Post-Migration Checks
&lt;/h3&gt;

&lt;p&gt;After boot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ip a&lt;/code&gt; — confirm interface (e.g., &lt;code&gt;ens3&lt;/code&gt;) has link and correct IP
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dmesg | grep -i virtio&lt;/code&gt; — verify &lt;code&gt;virtio_net&lt;/code&gt;, &lt;code&gt;virtio_blk&lt;/code&gt; loaded
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lsmod | grep -E "(vmxnet3|vmmouse)"&lt;/code&gt; — ensure VMware drivers are absent
&lt;/li&gt;
&lt;li&gt;Test SSH, service uptime, and baseline performance &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point, the &lt;em&gt;vm migrate from vmware to kvm&lt;/em&gt; process is complete — with a fully operational guest.&lt;/p&gt;




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

&lt;p&gt;Migrating VMs from VMware to KVM is more than a cost play — it's about adopting open, auditable infrastructure. virt-v2v enables this transition not through brute-force copying, but by integrating deeply with libvirt, QEMU, and libguestfs to transform guest configuration at the kernel level.&lt;/p&gt;

&lt;p&gt;The tool doesn’t abstract complexity — it applies it correctly. You’re not relocating a VM; you’re converting its hardware identity from VMware to KVM. That involves device drivers, initramfs, bootloader logic, and registry entries on Windows. Skipping this (e.g., using &lt;code&gt;qemu-img convert&lt;/code&gt;) results in boot failures, undetected disks, or degraded I/O.&lt;/p&gt;

&lt;p&gt;When you &lt;em&gt;vm migrate from vmware to kvm&lt;/em&gt; using virt-v2v, the result isn’t a ported VM — it’s a native one, indistinguishable from a guest installed directly on KVM.&lt;/p&gt;

&lt;h2&gt;
  
  
  ❓ Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Can I convert VMs without powering them off?
&lt;/h3&gt;

&lt;p&gt;Yes, with VDDK. The VMware Virtual Disk Development Kit allows hot reading of .vmdk files, so the source VM can stay powered on during migration. However, only data present at the start of the transfer is captured unless application-consistent snapshots are used.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does virt-v2v support encrypted VMware VMs?
&lt;/h3&gt;

&lt;p&gt;No. VMware VM encryption (VMCE) is not supported by VDDK in offline mode. The VM must be decrypted in vCenter before conversion.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I automate migration of multiple VMs?
&lt;/h3&gt;

&lt;p&gt;Yes. Use the vSphere API or &lt;code&gt;vim-cmd&lt;/code&gt; to enumerate VMs, then script virt-v2v calls in a loop. Pair with SSH key authentication and shared storage (e.g., NFS) for efficient, scalable migrations.&lt;/p&gt;

&lt;h2&gt;
  
  
  📚 References &amp;amp; Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;VMware VDDK documentation — API and deployment guide for high-speed disk access: &lt;a href="https://www.vmware.com/support/developer/vddk/" rel="noopener noreferrer"&gt;docs.vmware.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>tutorial</category>
      <category>cloud</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>☁️ Mastering gcp vpc peering setup tutorial made easy</title>
      <dc:creator>Python-T Point</dc:creator>
      <pubDate>Tue, 12 May 2026 03:44:52 +0000</pubDate>
      <link>https://dev.to/ptp2308/mastering-gcp-vpc-peering-setup-tutorial-made-easy-1893</link>
      <guid>https://dev.to/ptp2308/mastering-gcp-vpc-peering-setup-tutorial-made-easy-1893</guid>
      <description>&lt;p&gt;About 70% of Google Cloud Platform (GCP) users operate across multiple projects, making cross-project networking a routine requirement. VPC peering is the standard mechanism to enable direct, private communication between resources in separate VPCs without routing traffic through the public internet. This setup is stable, low-latency, and suitable for most intra-organization workloads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;📑 Table of Contents&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;💻 GCP VPC Peering — What is &lt;em&gt;Peering&lt;/em&gt;?&lt;/li&gt;
&lt;li&gt;🔑 Benefits of VPC Peering&lt;/li&gt;
&lt;li&gt;📦 Setting Up VPC Peering — Step by Step&lt;/li&gt;
&lt;li&gt;📝 Updating Network Configuration&lt;/li&gt;
&lt;li&gt;🔍 Verifying the Connection&lt;/li&gt;
&lt;li&gt;🔧 Troubleshooting Common Issues&lt;/li&gt;
&lt;li&gt;📊 Best Practices for VPC Peering&lt;/li&gt;
&lt;li&gt;🟩 Final Thoughts&lt;/li&gt;
&lt;li&gt;❓ Frequently Asked Questions&lt;/li&gt;
&lt;li&gt;What is VPC peering?&lt;/li&gt;
&lt;li&gt;How do I set up VPC peering?&lt;/li&gt;
&lt;li&gt;What are the benefits of VPC peering?&lt;/li&gt;
&lt;li&gt;📚 References &amp;amp; Further Reading&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  💻 GCP VPC Peering — What is &lt;em&gt;Peering&lt;/em&gt;?
&lt;/h2&gt;

&lt;p&gt;GCP VPC peering establishes a direct network connection between two Virtual Private Clouds (VPCs), allowing resources in either network to communicate using internal IP addresses. The connection is regional: routes are exchanged automatically within each VPC, but only for subnets whose IP ranges do not overlap.&lt;/p&gt;

&lt;p&gt;Peering is non-transitive. If VPC A is peered with VPC B, and VPC B is peered with VPC C, traffic from A cannot reach C through B. This isolation prevents unintended lateral access and enforces explicit network design.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔑 Benefits of VPC Peering
&lt;/h3&gt;

&lt;p&gt;The primary benefit is secure, low-latency communication across project boundaries — ideal for microservices, databases, and shared infrastructure. Because traffic stays within Google's network, it avoids public exposure and benefits from built-in encryption at the PHY layer. Latency remains consistent and typically under 2ms in the same region.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcloud compute networks peerings list
# Lists all VPC peering connections in your project



NAME                 NETWORK           PEER_NETWORK                  PEER_PROJECT    STATE
my-peering-connection my-network        my-peer-network                my-project       ACTIVE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  📦 Setting Up VPC Peering — Step by Step
&lt;/h2&gt;

&lt;p&gt;To peer two VPCs, both networks must have non-overlapping CIDR ranges. One project initiates the peering request; the other accepts it. The setup requires IAM permissions: &lt;code&gt;compute.networkAdmin&lt;/code&gt; in both projects.&lt;/p&gt;

&lt;p&gt;First, create the peering connection from one side. Replace the full URL path with your peer project ID and network name.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcloud compute networks peerings create my-peering-connection \
  --network my-network \
  --peer-network https://www.googleapis.com/compute/v1/projects/my-project/global/networks/my-peer-network
# Creates a new VPC peering connection
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then, run the same command in the peer project, unless using a Shared VPC or an automated pipeline. Once initiated, the peering state transitions to &lt;code&gt;PENDING_ACCEPTANCE&lt;/code&gt;. The peer project must accept it explicitly.&lt;/p&gt;

&lt;h3&gt;
  
  
  📝 Updating Network Configuration
&lt;/h3&gt;

&lt;p&gt;After peering is established, configure firewall rules to allow traffic. By default, all traffic is blocked. Rules must be applied in both VPCs if bidirectional communication is needed.&lt;/p&gt;

&lt;p&gt;Use network tags or service accounts to scope rules tightly. For example, allow HTTP traffic only from instances tagged as &lt;code&gt;web-tier&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcloud compute firewall-rules create my-firewall-rule \
  --network my-network \
  --allow tcp:80 \
  --source-ranges 10.128.0.0/9
# Authorizes TCP port 80 from peer VPC's IP range



Creating firewall... Done.
NAME                NETWORK       DIRECTION  PRIORITY  ALLOW     DENY  DISABLED
my-firewall-rule    my-network    INGRESS    1000      tcp:80          False
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  🔍 Verifying the Connection
&lt;/h3&gt;

&lt;p&gt;Test connectivity using &lt;code&gt;ping&lt;/code&gt; or tools like &lt;code&gt;telnet&lt;/code&gt; and &lt;code&gt;nc&lt;/code&gt;. Ensure the target instance has internal connectivity and the correct firewall rules. &lt;em&gt;(More on&lt;a href="https://pythontpoint.in" rel="noopener noreferrer"&gt;PythonTPoint tutorials&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ping -c 1 10.132.0.5
# Tests connectivity to an instance in the peer VPC



PING 10.132.0.5 (10.132.0.5) 56(84) bytes of data.
64 bytes from 10.132.0.5: icmp_seq=1 ttl=64 time=0.921 ms

--- 10.132.0.5 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.921/0.921/0.921/0.000 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  🔧 Troubleshooting Common Issues
&lt;/h2&gt;

&lt;p&gt;Most issues stem from overlapping CIDR blocks, missing firewall rules, or unaccepted peering requests. Check the peering status first.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcloud compute networks peerings describe my-peering-connection --network my-network
# Displays detailed information about the peering connection



name: my-peering-connection
network: https://www.googleapis.com/compute/v1/projects/my-project/global/networks/my-network
peerNetwork: https://www.googleapis.com/compute/v1/projects/peer-project/global/networks/my-peer-network
state: ACTIVE
stateDetails: ''
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If state is &lt;code&gt;INACTIVE&lt;/code&gt;, confirm that both sides have completed setup and CIDR ranges do not overlap. Use &lt;code&gt;gcloud compute networks list&lt;/code&gt; to audit IP ranges. (Also read: &lt;a href="https://pythontpoint.in/docker-compose-django-postgres-tutorial-setup-made-simple/" rel="noopener noreferrer"&gt;🚀 Docker Compose Django Postgres tutorial — setup made simple&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;For connectivity issues, verify that the target instance has a running service and that firewall rules allow the port. Use VPC Flow Logs to inspect allowed and denied traffic.&lt;/p&gt;

&lt;h2&gt;
  
  
  📊 Best Practices for VPC Peering
&lt;/h2&gt;

&lt;p&gt;Plan your CIDR allocation carefully. Use a structured IP address plan (e.g., 10.128.0.0/9 for services, 10.132.0.0/10 for GKE) to avoid conflicts as the environment scales.&lt;/p&gt;

&lt;p&gt;Prefer hierarchical firewall policies via Organization Policies when managing multiple projects. This ensures consistent rule enforcement and reduces configuration drift.&lt;/p&gt;

&lt;p&gt;Monitor peering connections via Cloud Monitoring. Alert on state changes using the &lt;code&gt;peerings/status&lt;/code&gt; metric. Downtime is rare but can occur during network reconfiguration or project deletion.&lt;/p&gt;




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

&lt;p&gt;GCP VPC peering is a reliable, performant way to connect resources across projects while keeping traffic private and secure. It requires precise configuration — especially around CIDR ranges and firewall rules — but operates with minimal overhead once established.&lt;/p&gt;

&lt;p&gt;For environments requiring transitive routing, consider using Cloud Router with VLAN attachments or a centralized transit VPC via Network Connectivity Center. But for direct, point-to-point connectivity, VPC peering remains the right choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  ❓ Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is VPC peering?
&lt;/h3&gt;

&lt;p&gt;VPC peering connects two GCP VPCs, enabling private communication using internal IPs. Traffic traverses Google's backbone, stays isolated from the public internet, and supports no additional egress cost.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I set up VPC peering?
&lt;/h3&gt;

&lt;p&gt;Create a peering request in one project, accept it in the peer project, then add firewall rules. Use &lt;code&gt;gcloud compute networks peerings create&lt;/code&gt; and ensure CIDR ranges do not overlap. Status must reach &lt;code&gt;ACTIVE&lt;/code&gt; on both ends.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are the benefits of VPC peering?
&lt;/h3&gt;

&lt;p&gt;It provides low-latency, secure, and cost-effective communication between VPCs in different projects or organizations. Latency is equivalent to same-VPC traffic, and throughput scales up to 50 Gbps per VM depending on machine type.&lt;/p&gt;

&lt;h2&gt;
  
  
  📚 References &amp;amp; Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Official GCP documentation for VPC peering — comprehensive guide to setting up and managing VPC peering connections: &lt;a href="https://cloud.google.com/vpc/docs/vpc-peering" rel="noopener noreferrer"&gt;cloud.google.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GCP VPC peering setup tutorial — step-by-step guide to setting up VPC peering between two projects: &lt;a href="https://cloud.google.com/vpc/docs/using-vpc-peering" rel="noopener noreferrer"&gt;cloud.google.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GCP networking documentation — detailed information on GCP networking features and best practices: &lt;a href="https://cloud.google.com/networking/docs" rel="noopener noreferrer"&gt;cloud.google.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>googlecloud</category>
      <category>cloud</category>
      <category>devops</category>
    </item>
    <item>
      <title>🐍 python args and kwargs explained simple — common mistakes and fixes</title>
      <dc:creator>Python-T Point</dc:creator>
      <pubDate>Mon, 11 May 2026 03:43:07 +0000</pubDate>
      <link>https://dev.to/ptp2308/python-args-and-kwargs-explained-simple-common-mistakes-and-fixes-4h1p</link>
      <guid>https://dev.to/ptp2308/python-args-and-kwargs-explained-simple-common-mistakes-and-fixes-4h1p</guid>
      <description>&lt;h2&gt;
  
  
  ❓ Can You Really Use *args and **kwargs Beyond Simple Examples?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ohe6ampohz3gnlwc67j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ohe6ampohz3gnlwc67j.png" alt="python args and kwargs explained simple" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The *args and **kwargs syntax in Python is not just about passing extra arguments; it's about writing functions that adapt to evolving interfaces, wrap other functions cleanly, and avoid brittle parameter lists in real codebases. Most tutorials stop at toy examples, leaving developers unsure how to apply them in production-grade code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;📑 Table of Contents&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❓ Can You Really Use *args and **kwargs Beyond Simple Examples?&lt;/li&gt;
&lt;li&gt;🐍 &lt;em&gt;args — Handling *Variable Positional&lt;/em&gt; Inputs&lt;/li&gt;
&lt;li&gt;🔧 Use Case: Flexible Logging Layers&lt;/li&gt;
&lt;li&gt;⚠️ Gotcha: Order Matters&lt;/li&gt;
&lt;li&gt;🧩 *&lt;em&gt;kwargs — Working with *Arbitrary Keyword&lt;/em&gt; Arguments&lt;/li&gt;
&lt;li&gt;🔧 Use Case: API Client Builders&lt;/li&gt;
&lt;li&gt;⚠️ Gotcha: Don’t Blindly Forward Unknown Kwargs&lt;/li&gt;
&lt;li&gt;🤝 Combining *args and **kwargs for Full Flexibility&lt;/li&gt;
&lt;li&gt;🔍 How Parameter Resolution Works&lt;/li&gt;
&lt;li&gt;⚙️ Unpacking with * and ** in Function Calls&lt;/li&gt;
&lt;li&gt;🧠 When to Use args and kwargs in Real Projects&lt;/li&gt;
&lt;li&gt;✅ Do Use Them For&lt;/li&gt;
&lt;li&gt;❌ Avoid Overusing When&lt;/li&gt;
&lt;li&gt;📚 Example: Flexible Class Initialization&lt;/li&gt;
&lt;li&gt;🟩 Final Thoughts&lt;/li&gt;
&lt;li&gt;❓ Frequently Asked Questions&lt;/li&gt;
&lt;li&gt;Can *args and **kwargs be used together in a function definition?&lt;/li&gt;
&lt;li&gt;Is there a performance cost to using *args and **kwargs?&lt;/li&gt;
&lt;li&gt;What happens if I pass a keyword argument that matches a named parameter and also include it in **kwargs?&lt;/li&gt;
&lt;li&gt;📚 References &amp;amp; Further Reading&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🐍 &lt;em&gt;args — Handling *Variable Positional&lt;/em&gt; Inputs
&lt;/h2&gt;

&lt;p&gt;The *args syntax lets a function accept any number of positional arguments, collected into a tuple. When Python sees the * prefix on a parameter, it tells the function to pack all remaining positional arguments into a tuple accessible by the given name. This is implemented at the C-level in CPython using PyArg_ParseTupleAndKeywords and related APIs — the interpreter dynamically builds the tuple from the call stack.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def log_action(user, action, *details):
    print(f"User '{user}' performed '{action}'")
    if details:
        print(f"Details: {', '.join(str(d) for d in details)}")

# Usage
log_action("alice", "file_upload", "report.pdf", "size: 2MB", "encrypted=True")



User 'alice' performed 'file_upload'
Details: report.pdf, size: 2MB, encrypted=True
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  🔧 Use Case: Flexible Logging Layers
&lt;/h3&gt;

&lt;p&gt;Functions that wrap actions — like audit logging in admin systems — often don’t know what arguments the wrapped function will receive. *args allows the wrapper to pass through all positional inputs untouched.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def audit_log(func):
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__} with args={args}, kwargs={kwargs}")
        return func(*args, **kwargs)
    return wrapper

@audit_log
def transfer_funds(from_id, to_id, amount, reason=None):
    print(f"Transferred ${amount} from {from_id} to {to_id}")

transfer_funds(101, 205, 500, reason="refund")



Calling transfer_funds with args=(101, 205, 500), kwargs={'reason': 'refund'}
Transferred $500 from 101 to 205
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  ⚠️ Gotcha: Order Matters
&lt;/h3&gt;

&lt;p&gt;*args consumes all unmatched positional arguments, so it must come after any required positional parameters. You can't define a function like def bad_func(*args, x) — Python raises a SyntaxError.&lt;/p&gt;


&lt;h2&gt;
  
  
  🧩 *&lt;em&gt;kwargs — Working with *Arbitrary Keyword&lt;/em&gt; Arguments
&lt;/h2&gt;

&lt;p&gt;The **kwargs syntax collects any unmatched keyword arguments into a dictionary. Mechanistically, when Python processes a function call, keyword arguments not matched to formal parameters are packed into a dict object. This is efficient for configuration-heavy workflows because dictionary lookups are O(1), and the structure mirrors JSON-like data common in APIs and config files.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def create_user(name, email, **profile):
    user = {"name": name, "email": email}
    user.update(profile)  # Add optional fields
    print(f"Created user: {user}")
    return user

# Usage
create_user("Bob", "bob@example.com", role="admin", team="infra", active=True)



Created user: {'name': 'Bob', 'email': 'bob@example.com', 'role': 'admin', 'team': 'infra', 'active': True}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  🔧 Use Case: API Client Builders
&lt;/h3&gt;

&lt;p&gt;When interfacing with REST APIs, query parameters or headers often vary by endpoint. Using **kwargs lets you write generic request wrappers.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import requests

def api_get(endpoint, **options):
    base_url = "https://api.example.com/v1"
    url = f"{base_url}/{endpoint}"

    # Extract specific keys, pass the rest as params
    headers = options.pop('headers', {})
    timeout = options.pop('timeout', 5)

    response = requests.get(url, params=options, headers=headers, timeout=timeout)
    return response.json() if response.ok else None

# Flexible calls
api_get("users", role="dev", active=True, timeout=10)
api_get("servers", region="us-west-2", headers={"Authorization": "Bearer xyz"})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This pattern keeps your interface clean while allowing full control over HTTP parameters — all without bloating the function signature.&lt;/p&gt;

&lt;h3&gt;
  
  
  ⚠️ Gotcha: Don’t Blindly Forward Unknown Kwargs
&lt;/h3&gt;

&lt;p&gt;Passing every unknown keyword argument directly to another system can introduce security or stability risks. Always validate or sanitize **kwargs when interfacing with external systems. (Also read: &lt;a href="https://pythontpoint.in/python-multiple-inheritance-examples-common-mistakes-and/" rel="noopener noreferrer"&gt;🐍 python multiple inheritance examples — common mistakes and how to fix them&lt;/a&gt;)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Use *args and **kwargs to defer decisions, not avoid design.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🤝 Combining *args and **kwargs for Full Flexibility
&lt;/h2&gt;

&lt;p&gt;A function can accept both *args and **kwargs, making it capable of wrapping any callable with any signature. (Also read: &lt;a href="https://pythontpoint.in/how-to-set-up-cicd-for-a-python-flask-app-using-github/" rel="noopener noreferrer"&gt;🐍 How to set up CI/CD for a Python Flask app using GitHub Actions — common mistakes and key tips&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;This combination is foundational in decorators, middleware, and proxy functions — especially in frameworks like Django, FastAPI, or Flask, where handlers need to remain agnostic to underlying signatures.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def retry_on_failure(max_retries=3):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for attempt in range(1, max_retries + 1):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    print(f"Attempt {attempt} failed: {e}")
                    if attempt == max_retries:
                        raise
            return None
        return wrapper
    return decorator

@retry_on_failure(max_retries=2)
def unstable_api_call(user_id):
    import random
    if random.random() &amp;lt; 0.7:
        raise ConnectionError("Network timeout")
    return {"status": "success", "data": f"profile_{user_id}"}

# Try calling
unstable_api_call(123)



Attempt 1 failed: Network timeout
Attempt 2 failed: Network timeout
...
# May eventually succeed or raise after 2 attempts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  🔍 How Parameter Resolution Works
&lt;/h3&gt;

&lt;p&gt;Python resolves function arguments in this order: (Also read: &lt;a href="https://pythontpoint.in/dockerfile-best-practices-python-flask-common-mistakes-and/" rel="noopener noreferrer"&gt;📦 Dockerfile best practices Python Flask — common mistakes and how to fix them&lt;/a&gt;)&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Positional arguments (matched to named parameters)&lt;/li&gt;
&lt;li&gt;Keyword arguments (by name)&lt;/li&gt;
&lt;li&gt;Default values for missing parameters&lt;/li&gt;
&lt;li&gt;*args collects unmatched positional arguments&lt;/li&gt;
&lt;li&gt;**kwargs collects unmatched keyword arguments&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The interpreter uses a stack frame to bind names, and the * and ** operators control how excess values are packed or unpacked.&lt;/p&gt;
&lt;h3&gt;
  
  
  ⚙️ Unpacking with * and ** in Function Calls
&lt;/h3&gt;

&lt;p&gt;Just as *args packs positional arguments during definition, using * in a function call unpacks a sequence into positional arguments.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;args = ["Alice", "edit_post", "post_id=456", "draft=True"]
log_action(*args)  # Equivalent to log_action("Alice", "edit_post", "post_id=456", "draft=True")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Similarly, ** unpacks a dictionary into keyword arguments:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kwargs = {
    "name": "Charlie",
    "email": "charlie@example.com",
    "role": "analyst",
    "department": "data"
}
create_user(**kwargs)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This bidirectional use — packing on definition, unpacking on call — is what makes the &lt;em&gt;args and **kwargs syntax so powerful in dynamic codebases. *(More on&lt;a href="https://pythontpoint.in" rel="noopener noreferrer"&gt;PythonTPoint tutorials&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 When to Use args and kwargs in Real Projects
&lt;/h2&gt;

&lt;p&gt;Knowing how to use *args and **kwargs is not enough — you need judgment about when to apply them.&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ Do Use Them For
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Decorators — they must work with any function signature.&lt;/li&gt;
&lt;li&gt;API wrappers — when forwarding arguments to another function or service.&lt;/li&gt;
&lt;li&gt;Base classes or mixins — passing arguments up the MRO via super().&lt;strong&gt;init&lt;/strong&gt;(*args, **kwargs).&lt;/li&gt;
&lt;li&gt;Configuration layers — where optional settings are passed down.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ❌ Avoid Overusing When
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The function has a clear, stable interface — explicit is better.&lt;/li&gt;
&lt;li&gt;You're hiding required parameters behind **kwargs — it hurts discoverability.&lt;/li&gt;
&lt;li&gt;You're building public APIs — users prefer autocomplete-friendly signatures.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  📚 Example: Flexible Class Initialization
&lt;/h3&gt;

&lt;p&gt;In inheritance hierarchies, *args and **kwargs let child classes pass arguments up without knowing the parent’s full signature.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Database:
    def __init__(self, host, port, **options):
        self.host = host
        self.port = port
        self.ssl = options.get("ssl", False)
        self.timeout = options.get("timeout", 30)

class MongoDatabase(Database):
    def __init__(self, db_name, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.db_name = db_name

# Usage
mongo = MongoDatabase(
    db_name="logs",
    host="10.0.1.100",
    port=27017,
    ssl=True,
    timeout=60
)
print(mongo.__dict__)



{'host': '10.0.1.100', 'port': 27017, 'ssl': True, 'timeout': 60, 'db_name': 'logs'}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This pattern is common in ORM models, SDKs, and configuration systems — and it’s a real-world example of why the *args and **kwargs syntax matters beyond syntax.&lt;/p&gt;




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

&lt;p&gt;*args and **kwargs are not just syntactic sugar — they’re tools for building adaptable, maintainable layers in Python applications. Used wisely, they reduce coupling between components, enable clean decorators, and simplify inheritance.&lt;/p&gt;

&lt;p&gt;However, like any dynamic feature, they trade off some clarity for flexibility. The key is knowing when to lock down an interface with explicit parameters, and when to leave it open using *args and **kwargs. In mature codebases, you’ll often see them used deep in infrastructure code — middleware, wrappers, base classes — while public APIs remain explicit and documented.&lt;/p&gt;

&lt;p&gt;Mastering the *args and **kwargs syntax means understanding both the mechanics and the design philosophy: defer decisions when you must, but document and constrain when you can.&lt;/p&gt;

&lt;h2&gt;
  
  
  ❓ Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Can *args and **kwargs be used together in a function definition?
&lt;/h3&gt;

&lt;p&gt;Yes — a function can accept both *args and **kwargs, provided they appear in the correct order: regular arguments, then *args, then keyword-only arguments or **kwargs. The syntax def func(a, *args, x=1, **kwargs): is valid and commonly used in frameworks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is there a performance cost to using *args and **kwargs?
&lt;/h3&gt;

&lt;p&gt;There is minimal overhead: *args creates a tuple, and **kwargs creates a dictionary. These are lightweight operations in CPython. The bigger concern is readability and debugging — stack traces and IDE hints may be less precise when arguments are hidden behind *args and **kwargs.&lt;/p&gt;

&lt;h3&gt;
  
  
  What happens if I pass a keyword argument that matches a named parameter and also include it in **kwargs?
&lt;/h3&gt;

&lt;p&gt;Python raises a TypeError for ambiguous assignments. For example, if a function has a parameter name, you can't pass name both as a positional/keyword argument and inside **kwargs. The interpreter resolves names strictly and prevents duplication.&lt;/p&gt;

&lt;h2&gt;
  
  
  📚 References &amp;amp; Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Official Python documentation on calls and definitions — covers *args and **kwargs in depth: &lt;a href="https://docs.python.org/3/tutorial/controlflow.html#more-on-defining-functions" rel="noopener noreferrer"&gt;docs.python.org&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Python data model reference for function call resolution: &lt;a href="https://docs.python.org/3/reference/expressions.html#calls" rel="noopener noreferrer"&gt;docs.python.org&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Real-world decorator patterns using *args and **kwargs: &lt;a href="https://docs.python.org/3/library/functools.html" rel="noopener noreferrer"&gt;docs.python.org&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>☁️ Terraform vs Pulumi: Which to choose for IaC in 2024?</title>
      <dc:creator>Python-T Point</dc:creator>
      <pubDate>Sun, 10 May 2026 03:43:01 +0000</pubDate>
      <link>https://dev.to/ptp2308/terraform-vs-pulumi-which-to-choose-for-iac-in-2024-1c7l</link>
      <guid>https://dev.to/ptp2308/terraform-vs-pulumi-which-to-choose-for-iac-in-2024-1c7l</guid>
      <description>&lt;p&gt;Two ways to define a cloud network — one using declarative HCL blocks, the other writing Python functions that provision AWS VPCs — can end up creating &lt;em&gt;the exact same infrastructure&lt;/em&gt;. Same subnets. Same route tables. Same security groups. Yet the paths to get there differ sharply in developer experience, tooling maturity, and team scalability. That’s the core of the &lt;strong&gt;terraform vs pulumi which to choose&lt;/strong&gt; debate in 2024.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;📑 Table of Contents&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🐍 Language &amp;amp; Syntax — Why &lt;em&gt;Expressiveness&lt;/em&gt; Matters&lt;/li&gt;
&lt;li&gt;🧠 State Management — How &lt;em&gt;Consistency&lt;/em&gt; Is Enforced&lt;/li&gt;
&lt;li&gt;🔧 Tooling &amp;amp; Debugging — Where &lt;em&gt;Developer Flow&lt;/em&gt; Differs&lt;/li&gt;
&lt;li&gt;⚙️ IDE Support&lt;/li&gt;
&lt;li&gt;🛠️ Testing&lt;/li&gt;
&lt;li&gt;🔄 CI/CD Integration&lt;/li&gt;
&lt;li&gt;🌍 Ecosystem &amp;amp; Adoption — What the &lt;em&gt;Job Market&lt;/em&gt; Rewards&lt;/li&gt;
&lt;li&gt;📦 Modules &amp;amp; Reusability — How &lt;em&gt;Abstraction&lt;/em&gt; Scales&lt;/li&gt;
&lt;li&gt;🔄 State Isolation&lt;/li&gt;
&lt;li&gt;🔐 Policy as Code&lt;/li&gt;
&lt;li&gt;🟩 Final Thoughts&lt;/li&gt;
&lt;li&gt;❓ Frequently Asked Questions&lt;/li&gt;
&lt;li&gt;Is Pulumi free to use?&lt;/li&gt;
&lt;li&gt;Can Pulumi replace Terraform completely?&lt;/li&gt;
&lt;li&gt;Do I need to learn Go to contribute to Pulumi providers?&lt;/li&gt;
&lt;li&gt;📚 References &amp;amp; Further Reading&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🐍 Language &amp;amp; Syntax — Why &lt;em&gt;Expressiveness&lt;/em&gt; Matters
&lt;/h2&gt;

&lt;p&gt;The most consequential difference between Terraform and Pulumi is the language abstraction.&lt;/p&gt;

&lt;p&gt;Terraform uses &lt;strong&gt;HashiCorp Configuration Language (HCL)&lt;/strong&gt; , a declarative, non-Turing-complete DSL designed for readability and structural predictability. It enforces separation between configuration and logic, limiting control flow to &lt;code&gt;count&lt;/code&gt;, &lt;code&gt;for_each&lt;/code&gt;, and &lt;code&gt;dynamic&lt;/code&gt; blocks. Pulumi uses general-purpose languages — &lt;strong&gt;Python, TypeScript, Go, or C#&lt;/strong&gt; — where infrastructure definitions are regular program statements.&lt;/p&gt;

&lt;p&gt;Consider an S3 bucket with versioning and AES-256 encryption.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "aws_s3_bucket" "logs" {
  bucket = "app-logs-prod-2024"

  versioning {
    enabled = true
  }

  server_side_encryption_configuration {
    rule {
      apply_server_side_encryption_by_default {
        sse_algorithm = "AES256"
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now the same setup in Pulumi with Python:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import pulumi
import pulumi_aws as aws

bucket = aws.s3.Bucket("logs", bucket="app-logs-prod-2024")

versioning = aws.s3.BucketVersioningV2("logs-versioning",
    bucket=bucket.id,
    versioning_configuration={
        "status": "Enabled"
    }
)

encryption = aws.s3.BucketServerSideEncryptionConfigurationV2("logs-encryption",
    bucket=bucket.id,
    server_side_encryption_configuration={
        "rules": [{
            "applyServerSideEncryptionByDefault": {
                "sseAlgorithm": "AES256"
            }
        }]
    }
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The Pulumi version behaves like application code. It supports loops, functions, type annotations, and standard testing tools. For example:&lt;/p&gt;

&lt;p&gt;"&lt;code&gt;python  &lt;br&gt;
buckets = []  &lt;br&gt;
for name in ["logs", "uploads", "backups"]:  &lt;br&gt;
b = aws.s3.Bucket(name, bucket=f"app-{name}-prod")  &lt;br&gt;
aws.s3.BucketVersioningV2(f"{name}-versioning", bucket=b.id, versioning_configuration={"status": "Enabled"})  &lt;br&gt;
buckets.append(b)  &lt;br&gt;
"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Terraform achieves repetition with &lt;code&gt;for_each&lt;/code&gt;, but logic remains bound to HCL’s expression syntax, which lacks function definitions and limits conditional nesting.&lt;/p&gt;

&lt;p&gt;Under the hood, both tools invoke the same &lt;strong&gt;provider binaries&lt;/strong&gt; — &lt;code&gt;terraform-provider-aws&lt;/code&gt; in plugin mode — and make identical HTTP calls to AWS APIs. The divergence is in abstraction level: Terraform keeps logic out of configuration; Pulumi embraces code as the source of truth.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Infrastructure as code shouldn’t mean writing in a language that can’t be tested like code."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For Indian engineering teams, this has material impact. Graduates are typically proficient in Python but unfamiliar with HCL. Pulumi reduces initial context switching. Terraform requires learning interpolation (&lt;code&gt;${var.name}&lt;/code&gt;), lifecycle rules, and &lt;code&gt;locals&lt;/code&gt; blocks — none of which transfer from general programming backgrounds.&lt;/p&gt;

&lt;p&gt;The key trade-off: Pulumi gains expressiveness at the cost of potential runtime complexity. Terraform trades flexibility for clearer static analysis.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 State Management — How &lt;em&gt;Consistency&lt;/em&gt; Is Enforced
&lt;/h2&gt;

&lt;p&gt;Both tools use a state file to map configuration to actual cloud resources.&lt;/p&gt;

&lt;p&gt;Terraform writes state to &lt;code&gt;terraform.tfstate&lt;/code&gt;, a JSON file that stores resource metadata, IDs, and dependencies. This file is essential for &lt;code&gt;plan&lt;/code&gt; and &lt;code&gt;apply&lt;/code&gt; operations. When using remote backends, state is stored in S3 or HashiCorp Consul, with DynamoDB locks to prevent concurrent writes.&lt;/p&gt;

&lt;p&gt;Pulumi stores state by default in a &lt;strong&gt;managed backend&lt;/strong&gt; (e.g., &lt;code&gt;s3://pulumi-state-bucket&lt;/code&gt;) or Pulumi Cloud. Local state is possible, but team workflows default to remote from the start. Each environment (dev, staging, prod) maps to a &lt;strong&gt;stack&lt;/strong&gt; , with configuration in &lt;code&gt;Pulumi.dev.yaml&lt;/code&gt;.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ pulumi up
Previewing update (dev)

View Live: https://app.pulumi.com/acme/project/dev/previews/abc123

 +  aws:s3:Bucket logs creating
 +  aws:s3:BucketVersioningV2 logs-versioning creating
 +  aws:s3:BucketServerSideEncryptionV2 logs-encryption creating
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Pulumi executes the entire program to build a dependency graph, then compares it with the prior state in the backend. This is different from Terraform, which parses HCL statically and evaluates expressions without executing arbitrary code.&lt;/p&gt;

&lt;p&gt;The consequence:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pulumi plans can run external logic (e.g., reading files, querying APIs), which increases flexibility but introduces risk if those operations fail during preview.
&lt;/li&gt;
&lt;li&gt;Terraform’s static evaluation avoids side effects but limits dynamic composition — for example, reading JSON config at runtime requires &lt;code&gt;file()&lt;/code&gt; interpolation, which can’t be used everywhere.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For organizations using shared state, Pulumi’s default remote backend reduces the chance of local state drift. However, Terraform’s S3 + DynamoDB pattern has handled enterprise-scale workloads since 2015, with predictable locking and audit trails via CloudTrail.&lt;/p&gt;

&lt;p&gt;Exact command to enable state locking in Terraform:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform {
  backend "s3" {
    bucket         = "my-terraform-state"
    key            = "global/s3/terraform.tfstate"
    region         = "ap-south-1"
    dynamodb_table = "terraform-locks"
    encrypt        = true
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This pattern remains the most widely adopted for cross-team collaboration.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔧 Tooling &amp;amp; Debugging — Where &lt;em&gt;Developer Flow&lt;/em&gt; Differs
&lt;/h2&gt;

&lt;p&gt;Debugging should reflect application development standards. Pulumi supports this. Terraform does not.&lt;/p&gt;

&lt;p&gt;HCL has no &lt;code&gt;print&lt;/code&gt; statements. No breakpoints. No stack traces. Debugging relies on &lt;code&gt;terraform console&lt;/code&gt; for expression testing and &lt;code&gt;TF_LOG=DEBUG&lt;/code&gt; to expose HTTP-level traffic.&lt;/p&gt;

&lt;p&gt;Pulumi runs in a real language runtime. You can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Insert &lt;code&gt;print()&lt;/code&gt; statements.
&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;pdb.set_trace()&lt;/code&gt; for interactive debugging.
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;mypy&lt;/code&gt; or &lt;code&gt;pylint&lt;/code&gt; in CI.
&lt;/li&gt;
&lt;li&gt;Write unit tests with &lt;code&gt;pytest&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import pdb; pdb.set_trace()
print(f"Resolved bucket name: {bucket_name}")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This integrates with IDEs like VS Code or PyCharm, enabling step-through inspection of variables and control flow — critical for developers learning AWS behavior or validating conditional logic.&lt;/p&gt;

&lt;p&gt;Terraform’s &lt;code&gt;TF_LOG&lt;/code&gt; output, while comprehensive, floods stdout with raw HTTP requests and provider internals. Filtering meaningful signals requires grepping through hundreds of lines.&lt;/p&gt;

&lt;h3&gt;
  
  
  ⚙️ IDE Support
&lt;/h3&gt;

&lt;p&gt;Pulumi benefits from mature language tooling. In Python, VS Code with Pylance provides autocomplete, hover docs, and refactoring for resource parameters. Type hints from &lt;code&gt;pulumi_aws&lt;/code&gt; catch misconfigurations early.&lt;/p&gt;

&lt;p&gt;Terraform’s IDE plugins offer syntax highlighting and basic validation. But HCL lacks deep typing. You won’t catch a misplaced block or invalid enum until &lt;code&gt;terraform validate&lt;/code&gt; or &lt;code&gt;plan&lt;/code&gt; runs.&lt;/p&gt;

&lt;h3&gt;
  
  
  🛠️ Testing
&lt;/h3&gt;

&lt;p&gt;Pulumi allows unit tests on infrastructure logic:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def test_bucket_naming():
    assert bucket.name.startswith("app-logs-")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Terraform has no native support for logic testing. &lt;code&gt;terraform validate&lt;/code&gt; checks syntax and schema conformance, but can’t verify naming rules or cross-resource constraints.&lt;/p&gt;

&lt;p&gt;Teams using CI/CD with quality gates find Pulumi easier to integrate with test pipelines, especially when enforcing organizational standards.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔄 CI/CD Integration
&lt;/h3&gt;

&lt;p&gt;Both tools work with GitHub Actions, GitLab CI, and Jenkins.&lt;/p&gt;

&lt;p&gt;Pulumi supports &lt;strong&gt;inline programs&lt;/strong&gt; in CI, where infrastructure code is defined directly in the pipeline YAML. This enables ephemeral environments per PR without requiring checked-in &lt;code&gt;.py&lt;/code&gt; files.&lt;/p&gt;

&lt;p&gt;Terraform requires &lt;code&gt;.tf&lt;/code&gt; files on disk. While this enforces version control discipline, it adds friction for dynamically generated environments.&lt;/p&gt;

&lt;p&gt;For short-lived staging setups, Pulumi’s inline capability reduces boilerplate and accelerates iteration.&lt;/p&gt;




&lt;h2&gt;
  
  
  🌍 Ecosystem &amp;amp; Adoption — What the &lt;em&gt;Job Market&lt;/em&gt; Rewards
&lt;/h2&gt;

&lt;p&gt;Terraform dominates enterprise cloud infrastructure in India.&lt;/p&gt;

&lt;p&gt;At firms from TCS to Zoho, and in regulated sectors like banking and telecom, Terraform is the default IaC tool. Job postings consistently list “Terraform + Ansible” as required skills. “Pulumi + Kubernetes” appears rarely.&lt;/p&gt;

&lt;p&gt;Reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Terraform launched in 2014; Pulumi in 2018. The adoption gap is real.
&lt;/li&gt;
&lt;li&gt;HashiCorp has deep training partnerships with Indian IT service providers.
&lt;/li&gt;
&lt;li&gt;AWS Certification paths emphasize Terraform patterns.
&lt;/li&gt;
&lt;li&gt;Most existing large-scale AWS deployments use Terraform state files and module registries.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But new trends favor Pulumi:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Startups with Python-first internal platforms adopt Pulumi to unify tooling.
&lt;/li&gt;
&lt;li&gt;Full-stack TypeScript teams extend their codebase to infrastructure without context switching.
&lt;/li&gt;
&lt;li&gt;DevOps engineers increasingly prioritize testability and debugging over config simplicity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For fresh graduates: Pulumi allows meaningful contribution with existing Python skills. Terraform requires learning HCL, state backends, module versioning, and workspace isolation — a nontrivial ramp.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;terraform vs pulumi which to choose&lt;/strong&gt; decision hinges on context:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Joining a legacy cloud team? Terraform is the baseline.
&lt;/li&gt;
&lt;li&gt;Launching a new product with a modern stack? Pulumi is production-ready.
&lt;/li&gt;
&lt;li&gt;Preparing for interviews? Know Terraform fundamentals. Demonstrate Pulumi if you can.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📦 Modules &amp;amp; Reusability — How &lt;em&gt;Abstraction&lt;/em&gt; Scales
&lt;/h2&gt;

&lt;p&gt;Both tools support reusable components, but model them differently.&lt;/p&gt;

&lt;p&gt;Terraform uses &lt;strong&gt;modules&lt;/strong&gt; — directories of &lt;code&gt;.tf&lt;/code&gt; files with defined inputs and outputs. Example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "3.14.0"

  name = "prod-vpc"
  cidr = "10.0.0.0/16"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;These are hosted on the &lt;strong&gt;Terraform Registry&lt;/strong&gt; , versioned with SemVer, and locked via &lt;code&gt;terraform.lock.hcl&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Pulumi uses &lt;strong&gt;components&lt;/strong&gt; — Python classes or functions that encapsulate resource creation.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class LogBucket(pulumi.ComponentResource):
    def __init__(self, name, opts=None):
        super().__init__('my:modules:LogBucket', name, {}, opts)
        self.bucket = aws.s3.Bucket(f"{name}-bucket")
        # ... attach policies, versioning, etc.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Components support inheritance, dependency injection, and mocking — features absent in HCL modules.&lt;/p&gt;

&lt;p&gt;For enterprise governance, Terraform’s isolation prevents logic sprawl. For innovation-speed teams, Pulumi’s code reuse accelerates development.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔄 State Isolation
&lt;/h3&gt;

&lt;p&gt;Terraform uses workspaces or separate directories for environment isolation. Each &lt;code&gt;dev&lt;/code&gt;, &lt;code&gt;staging&lt;/code&gt;, &lt;code&gt;prod&lt;/code&gt; setup has its own state file.&lt;/p&gt;

&lt;p&gt;Pulumi uses &lt;strong&gt;stacks&lt;/strong&gt;. Configuration is stored in &lt;code&gt;Pulumi.dev.yaml&lt;/code&gt;, &lt;code&gt;Pulumi.prod.yaml&lt;/code&gt;, and selected via:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ pulumi stack select dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This mirrors environment variable patterns in app development, reducing cognitive load.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔐 Policy as Code
&lt;/h3&gt;

&lt;p&gt;Terraform integrates with &lt;strong&gt;Sentinel&lt;/strong&gt; (closed-source) and &lt;strong&gt;Open Policy Agent (OPA)&lt;/strong&gt; for policy enforcement. Policies run during &lt;code&gt;plan&lt;/code&gt; checks in Terraform Cloud.&lt;/p&gt;

&lt;p&gt;Pulumi uses &lt;strong&gt;CrossGuard&lt;/strong&gt; , a policy-as-code framework supporting Python and TypeScript rules, or integrates with OPA.&lt;/p&gt;

&lt;p&gt;In practice, most Indian teams skip full policy engines and rely on CI checks or pre-commit hooks. The gap in real-world usage is negligible.&lt;/p&gt;




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

&lt;p&gt;The &lt;strong&gt;terraform vs pulumi which to choose&lt;/strong&gt; question has no universal answer — but a clear contextual one for Indian developers in 2024.&lt;/p&gt;

&lt;p&gt;Terraform remains the safe career investment. It's embedded in enterprise hiring, certification, and legacy systems. Mastering it grants immediate access to production cloud environments.&lt;/p&gt;

&lt;p&gt;Pulumi aligns with modern software engineering practices. For teams already using Python or TypeScript, it eliminates the need to learn a domain-specific config language. Testing, debugging, and refactoring apply directly to infrastructure definitions.&lt;/p&gt;

&lt;p&gt;The trend is clear: infrastructure is code, not just configuration. And code should be executable, testable, and maintainable.&lt;/p&gt;

&lt;p&gt;So if you're starting out, learn both. Use Terraform to pass interviews and understand declarative workflows. Build side projects with Pulumi to experience the evolution of IaC.&lt;/p&gt;

&lt;p&gt;Your goal isn't loyalty to a tool. It's understanding the trade-offs: safety versus expressiveness, adoption versus agility.&lt;/p&gt;

&lt;p&gt;In India’s fast-changing tech landscape, that depth of judgment defines not just execution, but architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  ❓ Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is Pulumi free to use?
&lt;/h3&gt;

&lt;p&gt;Pulumi is open-source and free for individual use. The CLI and core SDKs are MIT-licensed. The Pulumi Cloud backend offers free tiers for small teams, with paid plans for advanced features like policy enforcement and audit logs. (Also read: &lt;a href="https://pythontpoint.in/github-vs-jenkins/" rel="noopener noreferrer"&gt;🚀 GitHub vs Jenkins — What’s the Real Difference?&lt;/a&gt;)&lt;/p&gt;

&lt;h3&gt;
  
  
  Can Pulumi replace Terraform completely?
&lt;/h3&gt;

&lt;p&gt;Yes, in most use cases. Pulumi supports all major cloud providers via the same underlying TF providers (using the &lt;strong&gt;shim layer&lt;/strong&gt;), so it can manage the same resources. Teams migrate from Terraform to Pulumi for better code reuse and debugging, though some miss HCL’s simplicity for small configs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need to learn Go to contribute to Pulumi providers?
&lt;/h3&gt;

&lt;p&gt;No. While Pulumi’s providers are written in Go, you don’t need to touch them to use Pulumi. For custom components, Python, TypeScript, or other host languages are sufficient. Only contributor-level work requires Go.&lt;/p&gt;

&lt;h2&gt;
  
  
  📚 References &amp;amp; Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Official Terraform documentation — comprehensive guide to HCL, state, and providers: &lt;a href="https://developer.hashicorp.com/terraform/docs" rel="noopener noreferrer"&gt;developer.hashicorp.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Infrastructure as Code best practices — from AWS Well-Architected Framework: &lt;a href="https://docs.aws.amazon.com/wellarchitected/latest/framework/infrastructure-as-code.html" rel="noopener noreferrer"&gt;docs.aws.amazon.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>tutorial</category>
      <category>cloud</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>🐍 How to set up CI/CD for a Python Flask app using GitHub Actions</title>
      <dc:creator>Python-T Point</dc:creator>
      <pubDate>Sat, 09 May 2026 03:37:42 +0000</pubDate>
      <link>https://dev.to/ptp2308/how-to-set-up-cicd-for-a-python-flask-app-using-github-actions-abj</link>
      <guid>https://dev.to/ptp2308/how-to-set-up-cicd-for-a-python-flask-app-using-github-actions-abj</guid>
      <description>&lt;p&gt;"Automate or stagnate" — a DevOps engineer I once paired with, halfway through a 40-minute deploy script.&lt;/p&gt;

&lt;p&gt;I didn’t get it at first. Then I spent three days debugging a Flask app that worked locally but failed silently in production. No logs. No tests. No repeatable deploy process — just a &lt;code&gt;git push&lt;/code&gt; and a prayer.&lt;/p&gt;

&lt;p&gt;That was the last time I treated deployment as an afterthought.&lt;/p&gt;

&lt;p&gt;Now I know: &lt;strong&gt;CI/CD&lt;/strong&gt; isn’t about speed. It’s about &lt;em&gt;predictability&lt;/em&gt;. For a Python Flask app, using &lt;strong&gt;GitHub Actions&lt;/strong&gt; to automate testing, linting, and deployment isn't optional — it’s the baseline for anything that needs to run reliably.&lt;/p&gt;

&lt;p&gt;A real &lt;strong&gt;python flask github actions ci cd&lt;/strong&gt; pipeline is more than a YAML file. It’s a chain of verifiable steps — testable, inspectable, and repeatable. When you push a commit, you should know exactly how your code gets built, tested, and deployed — and what happens when something fails.&lt;/p&gt;

&lt;p&gt;This post walks through building that pipeline: from a minimal Flask app to a full workflow that validates every change and deploys only when everything passes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd4edkap4d0a8bv1tov2v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd4edkap4d0a8bv1tov2v.png" alt="python flask github actions ci cd" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🐍 Flask App — Your &lt;em&gt;Foundation&lt;/em&gt; Starts Here
&lt;/h2&gt;

&lt;p&gt;A CI/CD pipeline only works if your app supports it.&lt;/p&gt;

&lt;p&gt;Every Flask project I start includes a clear entry point, a &lt;code&gt;requirements.txt&lt;/code&gt;, and a test suite. Here’s the minimal layout that works across teams and environments:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;myflaskapp/
├── app.py
├── requirements.txt
├── tests/
│   └── test_routes.py
└── .github/workflows/ci-cd.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;app.py&lt;/code&gt; defines a basic route:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from flask import Flask

app = Flask(__name__)

@app.route("/")
def home():
    return {"status": "ok", "message": "Hello from Flask!"}

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;requirements.txt&lt;/code&gt; pins versions:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Flask==3.0.3
pytest==8.2.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And &lt;code&gt;tests/test_routes.py&lt;/code&gt; ensures correctness:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import pytest
from app import app

@pytest.fixture
def client():
    app.config['TESTING'] = True
    with app.test_client() as client:
        yield client

def test_home_route(client):
    response = client.get("/")
    assert response.status_code == 200
    json_data = response.get_json()
    assert json_data['status'] == 'ok'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Run locally:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ python -m pytest
============================= test session starts ==============================
platform linux -- Python 3.11.9, pytest-8.2.2, pluggy-1.5.0
rootdir: /home/user/myflaskapp
collected 1 item

tests/test_routes.py .                                                   [100%]

============================== 1 passed in 0.12s ===============================
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This same command runs in CI. If it passes here, it will pass there — assuming the environment is consistent.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚙️ GitHub Actions — How the &lt;em&gt;Pipeline&lt;/em&gt; Works
&lt;/h2&gt;

&lt;p&gt;A GitHub Actions workflow is a declarative script that runs in response to code changes.&lt;/p&gt;

&lt;p&gt;When you push to a branch or open a PR, GitHub starts a fresh &lt;strong&gt;runner&lt;/strong&gt; — an ephemeral Ubuntu VM — and runs your steps. No shared state. No lingering packages. Just a clean environment every time.&lt;/p&gt;

&lt;p&gt;Here’s the core workflow in &lt;code&gt;.github/workflows/ci-cd.yml&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name: CI/CD Pipeline

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'

      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt

      - name: Run tests
        run: python -m pytest

      - name: Lint with flake8
        run: |
          pip install flake8
          flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;What happens, step by step:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;actions/checkout@v4&lt;/code&gt; clones the repo using Git over HTTPS. It’s a lightweight composite action — no Docker, no overhead.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;actions/setup-python@v5&lt;/code&gt; installs Python 3.11 via &lt;code&gt;pyenv&lt;/code&gt;, caching it for future runs. The version is isolated to the job.
&lt;/li&gt;
&lt;li&gt;Dependency installation runs in a fresh shell. No global site-packages. No accidental reliance on system packages.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pytest&lt;/code&gt; runs in the same context, so it sees the installed deps.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;flake8&lt;/code&gt; catches syntax errors and common anti-patterns — like &lt;code&gt;F821 undefined name&lt;/code&gt; — before code is merged.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If any step fails, the pipeline stops. No merge. No deployment.&lt;/p&gt;

&lt;p&gt;Output from a passing run:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Ran 1 test in 0.123s
OK
flake8: 0 errors, 0 warnings
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This output is logged and surfaced in the PR. You don’t need to run anything locally.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔍 Understanding the Runner Environment
&lt;/h3&gt;

&lt;p&gt;GitHub runners are disposable Ubuntu 22.04 VMs. Each job starts clean — no pip cache, no Git history, no environment variables beyond defaults.&lt;/p&gt;

&lt;p&gt;That means &lt;code&gt;pip install&lt;/code&gt; downloads every package from PyPI on every run — unless you cache.&lt;/p&gt;

&lt;p&gt;Add this step to cut install time from ~30s to ~5s:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- name: Cache pip
  uses: actions/cache@v4
  with:
    path: ~/.cache/pip
    key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The cache key includes the OS and the hash of &lt;code&gt;requirements.txt&lt;/code&gt;. If the file changes, the cache invalidates.&lt;/p&gt;

&lt;p&gt;This works because &lt;code&gt;pip&lt;/code&gt; stores downloaded wheels in &lt;code&gt;~/.cache/pip&lt;/code&gt; by default. GitHub Actions caches that directory between runs — safely, per-branch.&lt;/p&gt;

&lt;h3&gt;
  
  
  🛠️ Handling Secrets and Environment Variables
&lt;/h3&gt;

&lt;p&gt;You’ll need secrets eventually — API keys, database credentials.&lt;/p&gt;

&lt;p&gt;Never hardcode them.&lt;/p&gt;

&lt;p&gt;Use GitHub’s repository &lt;strong&gt;Secrets&lt;/strong&gt; UI:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a secret named &lt;code&gt;PROD_API_KEY&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Reference it in your workflow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;name: Deploy to production
env:
API_KEY: ${{ secrets.PROD_API_KEY }}
run: ./deploy.sh&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;These values are injected at runtime, encrypted in transit and at rest. They never appear in logs — even if you &lt;code&gt;echo $API_KEY&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;GitHub masks secrets automatically in job output.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 Deployment — When &lt;em&gt;Automate&lt;/em&gt; Meets &lt;em&gt;Ship&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;CI verifies. CD deploys.&lt;/p&gt;

&lt;p&gt;Extend the pipeline to deploy on &lt;code&gt;main&lt;/code&gt; after tests pass.&lt;/p&gt;

&lt;p&gt;Assume a VPS running Nginx + Gunicorn. The deploy job should pull code, install dependencies, and reload the app.&lt;/p&gt;

&lt;p&gt;Here’s the job:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;deploy:
  needs: test
  runs-on: ubuntu-latest
  if: github.ref == 'refs/heads/main'
  steps:
    - name: Deploy to production
      uses: appleboy/ssh-action@v1.0.1
      with:
        host: ${{ secrets.HOST }}
        username: ${{ secrets.USER }}
        key: ${{ secrets.SSH_KEY }}
        script: |
          cd /var/www/myflaskapp
          git pull origin main
          source venv/bin/activate
          pip install -r requirements.txt
          sudo systemctl restart gunicorn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;needs: test&lt;/code&gt; ensures this only runs if tests pass. The &lt;code&gt;if&lt;/code&gt; condition restricts it to &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But raw SSH has risks. A typo in &lt;code&gt;script&lt;/code&gt; could break the app or lock you out.&lt;/p&gt;

&lt;p&gt;So:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use a deploy key with read-only access to the repo
&lt;/li&gt;
&lt;li&gt;Restrict SSH to GitHub’s IP ranges via firewall
&lt;/li&gt;
&lt;li&gt;Test the deploy script locally before automating it&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🛡️ Safer Alternatives: Use Deploy Scripts
&lt;/h3&gt;

&lt;p&gt;Inline scripts in YAML are hard to test and version.&lt;/p&gt;

&lt;p&gt;Instead, check in a deploy script:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash
set -e  # Exit on any failure

cd /var/www/myflaskapp
git fetch origin
git reset --hard origin/main

source venv/bin/activate
pip install -r requirements.txt

# Trigger Gunicorn reload without downtime
touch app.wsgi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then call it from the workflow:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;script: bash /var/www/myflaskapp/deploy.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;set -e&lt;/code&gt; ensures the script halts at the first error. No half-updated deploys.&lt;/p&gt;

&lt;h3&gt;
  
  
  🌐 Zero-Downtime Deployments? Start Simple
&lt;/h3&gt;

&lt;p&gt;You might worry about downtime during &lt;code&gt;pip install&lt;/code&gt; or &lt;code&gt;systemctl restart&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For most Flask apps, a sub-second gap is acceptable.&lt;/p&gt;

&lt;p&gt;If it’s not, then consider process managers like &lt;code&gt;supervisord&lt;/code&gt;, rolling restarts with Gunicorn workers, or container orchestration — but only when monitoring shows it’s needed.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Automate the common case first. Optimize the edge case only when it becomes the norm.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  🧪 Testing Strategy — &lt;em&gt;Beyond&lt;/em&gt; "It Works on My Machine"
&lt;/h2&gt;

&lt;p&gt;A pipeline is only as good as its tests.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;pytest&lt;/code&gt; job runs unit tests — fast and isolated. But that’s not enough.&lt;/p&gt;

&lt;p&gt;Add layers:&lt;/p&gt;

&lt;p&gt;1. &lt;strong&gt;Unit tests&lt;/strong&gt; — verify logic (like &lt;code&gt;test_home_route&lt;/code&gt;)&lt;br&gt;&lt;br&gt;
2. &lt;strong&gt;Integration tests&lt;/strong&gt; — check component interactions&lt;br&gt;&lt;br&gt;
3. &lt;strong&gt;Static analysis&lt;/strong&gt; — catch bugs before execution&lt;/p&gt;

&lt;p&gt;For integration, test the app as a running service:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import threading
import time
import requests
from app import app

def test_integration_live_server():
    server = threading.Thread(target=lambda: app.run(port=5000))
    server.daemon = True
    server.start()
    time.sleep(1)

    response = requests.get("http://localhost:5000/")
    assert response.status_code == 200
    assert response.json()['status'] == 'ok'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is slower, so mark it with &lt;code&gt;pytest.mark.slow&lt;/code&gt; and skip it locally with &lt;code&gt;-m "not slow"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For static analysis, add &lt;code&gt;mypy&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install mypy
mypy app.py --strict
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;It catches type mismatches — like passing a string where an int is expected.&lt;/p&gt;

&lt;p&gt;And &lt;code&gt;bandit&lt;/code&gt; for security:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install bandit
bandit -r app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;It flags dangerous patterns — &lt;code&gt;pickle&lt;/code&gt;, &lt;code&gt;eval&lt;/code&gt;, hardcoded passwords.&lt;/p&gt;

&lt;p&gt;Add both to the workflow:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- name: Type check
  run: |
    pip install mypy
    mypy app.py --strict

- name: Security scan
  run: |
    pip install bandit
    bandit -r .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now the pipeline doesn’t just verify behavior — it enforces quality and safety.&lt;/p&gt;

&lt;h3&gt;
  
  
  🎯 Why This Matters: The Mechanism Behind Confidence
&lt;/h3&gt;

&lt;p&gt;When you push, GitHub Actions:&lt;/p&gt;

&lt;p&gt;1. Starts a fresh Ubuntu runner (no persistent state)&lt;br&gt;&lt;br&gt;
2. Clones the repo using &lt;code&gt;actions/checkout@v4&lt;/code&gt;&lt;br&gt;&lt;br&gt;
3. Installs Python 3.11 via &lt;code&gt;setup-python@v5&lt;/code&gt; (using &lt;code&gt;pyenv&lt;/code&gt;)&lt;br&gt;&lt;br&gt;
4. Installs deps with &lt;code&gt;pip&lt;/code&gt;, optionally cached&lt;br&gt;&lt;br&gt;
5. Runs &lt;code&gt;pytest&lt;/code&gt;, &lt;code&gt;flake8&lt;/code&gt;, &lt;code&gt;mypy&lt;/code&gt;, &lt;code&gt;bandit&lt;/code&gt; in order&lt;br&gt;&lt;br&gt;
6. Reports results via GitHub’s Checks API&lt;/p&gt;

&lt;p&gt;Each step is defined in code. The environment is explicit. There’s no hidden config.&lt;/p&gt;

&lt;p&gt;This reproducibility is what makes CI trustworthy.&lt;/p&gt;

&lt;p&gt;Compare that to “works on my machine”: a custom Python version, global packages, local &lt;code&gt;.env&lt;/code&gt; files. Those don’t survive handoffs.&lt;/p&gt;

&lt;p&gt;GitHub Actions removes that variability — not by magic, but by treating the build environment as disposable and versioned.&lt;/p&gt;

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

&lt;p&gt;A &lt;strong&gt;python flask github actions ci cd&lt;/strong&gt; pipeline isn’t about tools. It’s about reducing uncertainty.&lt;/p&gt;

&lt;p&gt;It forces a simple question: &lt;em&gt;Can this app be built, tested, and deployed by a machine that knows nothing about the developer?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If yes, you’ve built something durable — something that outlives laptops, onboarding, and team changes.&lt;/p&gt;

&lt;p&gt;I used to dread deploys. Now I merge with confidence. Because when the pipeline turns green, it’s not luck — it’s proof.&lt;/p&gt;

&lt;p&gt;That shift — from hope to verification — is what turns side projects into systems people depend on.&lt;/p&gt;

&lt;h2&gt;
  
  
  ❓ Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Can I use GitHub Actions for free?
&lt;/h3&gt;

&lt;p&gt;Yes. GitHub offers free CI/CD minutes for public repositories and limited minutes for private repos under the free plan. Usage scales with paid plans.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;📑 Table of Contents&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🐍 Flask App — Your &lt;em&gt;Foundation&lt;/em&gt; Starts Here&lt;/li&gt;
&lt;li&gt;⚙️ GitHub Actions — How the &lt;em&gt;Pipeline&lt;/em&gt; Works&lt;/li&gt;
&lt;li&gt;🔍 Understanding the Runner Environment&lt;/li&gt;
&lt;li&gt;🛠️ Handling Secrets and Environment Variables&lt;/li&gt;
&lt;li&gt;🚀 Deployment — When &lt;em&gt;Automate&lt;/em&gt; Meets &lt;em&gt;Ship&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;🛡️ Safer Alternatives: Use Deploy Scripts&lt;/li&gt;
&lt;li&gt;🌐 Zero-Downtime Deployments? Start Simple&lt;/li&gt;
&lt;li&gt;🧪 Testing Strategy — &lt;em&gt;Beyond&lt;/em&gt; "It Works on My Machine"&lt;/li&gt;
&lt;li&gt;🎯 Why This Matters: The Mechanism Behind Confidence&lt;/li&gt;
&lt;li&gt;🟩 Final Thoughts&lt;/li&gt;
&lt;li&gt;❓ Frequently Asked Questions&lt;/li&gt;
&lt;li&gt;Can I use GitHub Actions for free?&lt;/li&gt;
&lt;li&gt;How do I debug a failed GitHub Actions job?&lt;/li&gt;
&lt;li&gt;Should I run migrations in the pipeline?&lt;/li&gt;
&lt;li&gt;📚 References &amp;amp; Further Reading&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How do I debug a failed GitHub Actions job?
&lt;/h3&gt;

&lt;p&gt;Click on the failed job in the Actions tab. Each step is expandable. Look at the logs — they show exact commands run and output. Use &lt;code&gt;echo&lt;/code&gt; statements or &lt;code&gt;set -x&lt;/code&gt; in scripts to trace execution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I run migrations in the pipeline?
&lt;/h3&gt;

&lt;p&gt;Not directly. Apply database migrations after deploy, not during CI. The pipeline should test code, not modify shared state. Use a separate, manual or gated step for migrations.&lt;/p&gt;

&lt;h2&gt;
  
  
  📚 References &amp;amp; Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Official Flask documentation — best practices for structuring and deploying Flask apps: &lt;a href="https://flask.palletsprojects.com/en/3.0.x/" rel="noopener noreferrer"&gt;flask.palletsprojects.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>tutorial</category>
      <category>cloud</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>⚙️ Jenkins vs GitHub Actions India — which one should you actually use?</title>
      <dc:creator>Python-T Point</dc:creator>
      <pubDate>Fri, 08 May 2026 03:47:14 +0000</pubDate>
      <link>https://dev.to/ptp2308/jenkins-vs-github-actions-india-which-one-should-you-actually-use-3ebl</link>
      <guid>https://dev.to/ptp2308/jenkins-vs-github-actions-india-which-one-should-you-actually-use-3ebl</guid>
      <description>&lt;p&gt;The choice between Jenkins and GitHub Actions for Indian startups depends on several factors, including team size, infrastructure maturity, and compliance needs. &lt;/p&gt;

&lt;p&gt;Jenkins is a self-hosted, open-source automation server that runs workflows as pipelines, defined either in the UI or via a Jenkinsfile. Its core strength lies in extreme flexibility through plugins and on-premise control. Jenkins uses a master-agent architecture, where the master node schedules jobs and manages the UI, while agent nodes execute the actual build steps. This allows for horizontal scaling, but also means that the team is responsible for securing, patching, and monitoring every piece.&lt;/p&gt;

&lt;p&gt;A minimal Jenkinsfile for a Python service might look like this:&lt;br&gt;&lt;br&gt;
"&lt;code&gt;groovy  &lt;br&gt;
pipeline {  &lt;br&gt;
agent { label 'python-node' }  &lt;br&gt;
stages {  &lt;br&gt;
stage('Clone') {  &lt;br&gt;
steps {  &lt;br&gt;
git 'https://github.com/yourorg/payment-service.git'  &lt;br&gt;
}  &lt;br&gt;
}  &lt;br&gt;
stage('Test') {  &lt;br&gt;
steps {  &lt;br&gt;
sh 'python -m pytest tests/'  &lt;br&gt;
}  &lt;br&gt;
}  &lt;br&gt;
stage('Deploy') {  &lt;br&gt;
steps {  &lt;br&gt;
sh 'ansible-playbook deploy-prod.yml -i inventory/prod'  &lt;br&gt;
}  &lt;br&gt;
}  &lt;br&gt;
}  &lt;br&gt;
}  &lt;br&gt;
"&lt;/code&gt;&lt;br&gt;&lt;br&gt;
When this pipeline runs, Jenkins pulls the repository using &lt;code&gt;git clone&lt;/code&gt;, allocates a workspace directory on the agent, executes each &lt;code&gt;sh&lt;/code&gt; command in a shell session, and reports logs back to the master. &lt;/p&gt;

&lt;p&gt;However, Jenkins' plugin ecosystem can be a double-edged sword. With over 1,800 plugins, some of which are abandoned or poorly maintained, the risk of a single buggy plugin crashing the entire master is real. I've seen this happen firsthand, where a pipeline failed due to an outdated plugin that broke after a Java upgrade. &lt;/p&gt;

&lt;p&gt;In contrast, GitHub Actions is a cloud-native CI/CD platform that integrates tightly with GitHub repositories. A workflow is defined in a &lt;code&gt;.github/workflows/ci.yml&lt;/code&gt; file, and jobs run on GitHub-hosted runners or self-hosted machines. The mechanism is event-driven, with every &lt;code&gt;git push&lt;/code&gt;, &lt;code&gt;pull_request&lt;/code&gt;, or &lt;code&gt;schedule&lt;/code&gt; triggering a webhook to GitHub's Actions service. &lt;/p&gt;

&lt;p&gt;Here's an example of the same pipeline in GitHub Actions:&lt;br&gt;&lt;br&gt;
"`yml&lt;br&gt;&lt;br&gt;
name: CI&lt;br&gt;&lt;br&gt;
on: [push, pull_request]&lt;br&gt;&lt;br&gt;
jobs:&lt;br&gt;&lt;br&gt;
test:&lt;br&gt;&lt;br&gt;
runs-on: ubuntu-latest&lt;br&gt;&lt;br&gt;
steps:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;uses: actions/checkout@v4
&lt;/li&gt;
&lt;li&gt;name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
&lt;/li&gt;
&lt;li&gt;name: Install deps
run: pip install -r requirements.txt
&lt;/li&gt;
&lt;li&gt;name: Run tests
run: python -m pytest tests/
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
&lt;/li&gt;
&lt;li&gt;uses: actions/checkout@v4
&lt;/li&gt;
&lt;li&gt;name: Configure AWS
uses: aws-actions/configure-aws-credentials@v2
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-south-1
&lt;/li&gt;
&lt;li&gt;name: Deploy
run: ansible-playbook deploy-prod.yml -i inventory/prod
"&lt;code&gt;  
When this workflow runs, GitHub spins up a fresh Ubuntu VM, executes each &lt;/code&gt;run` step in a clean shell, and injects secrets at runtime via encrypted environment variables. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In terms of regional runner latency in India, GitHub's public runners are hosted in AWS us-east-1, us-west-1, and eu-west-1, which can result in ~200ms latency. However, this can be mitigated by caching dependencies across runs:&lt;br&gt;&lt;br&gt;
"`yml  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;name: Cache pip
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
"&lt;code&gt;  
This reduces &lt;/code&gt;pip install` time from 45 seconds to ~8 seconds in repeat builds. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For secrets management, GitHub Actions has stronger default security, with secrets encrypted and scoped to repositories. In contrast, Jenkins stores credentials in files or the database, which can be vulnerable if the master is compromised. &lt;/p&gt;

&lt;p&gt;So, how should you choose between Jenkins and GitHub Actions? It ultimately depends on your team size, infrastructure maturity, and compliance needs. &lt;/p&gt;

&lt;p&gt;If you're in fintech, healthcare, or govtech and need on-premise CI, or if your stack relies on legacy tools, Jenkins might be the better choice. On the other hand, if you're a fast-moving SaaS startup with a remote or distributed team, GitHub Actions might be the way to go. &lt;/p&gt;

&lt;p&gt;As a junior developer in India, it's essential to learn both tools and understand their trade-offs. Start with GitHub Actions, which is easier to pick up and integrates with tools you already use. Then, learn Jenkins to understand how automation servers work under the hood. &lt;/p&gt;

&lt;p&gt;Here's a 4-week plan to get you started:&lt;br&gt;&lt;br&gt;
1. Set up a GitHub repository with a Python or Node.js app and write a CI workflow that runs tests and lints code.&lt;br&gt;&lt;br&gt;
2. Add a self-hosted runner on an EC2 instance and see how jobs route between cloud and on-prem.&lt;br&gt;&lt;br&gt;
3. Install Jenkins locally via Docker and create a pipeline that builds a Docker image and pushes it to Docker Hub.&lt;br&gt;&lt;br&gt;
4. Migrate the Jenkins pipeline to GitHub Actions and compare config complexity, debug time, and execution speed. &lt;/p&gt;

&lt;p&gt;By the end of this plan, you'll have a solid understanding of both tools and be able to make informed decisions about which one to use for your projects. &lt;/p&gt;

&lt;p&gt;In Indian tech interviews, be prepared to answer questions about securing secrets in Jenkins, handling runner failures in GitHub Actions, and debugging flaky tests. These questions are designed to test your understanding of system design and problem-solving skills. &lt;/p&gt;

&lt;p&gt;Ultimately, the choice between Jenkins and GitHub Actions is not a zero-sum game. It's about depth and understanding the trade-offs between control, convenience, cost, and speed. By learning both tools and their underlying principles, you'll become a more valuable engineer who can solve problems, not just memorize syntax. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmw2d2y86y4zexykukcfp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmw2d2y86y4zexykukcfp.png" alt="jenkins vs github actions india" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;The debate between Jenkins and GitHub Actions is not about which tool is better, but about understanding the trade-offs and making informed decisions. &lt;/p&gt;

&lt;h2&gt;
  
  
  ❓ Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is GitHub Actions free for startups?
&lt;/h3&gt;

&lt;p&gt;Yes, for public repositories. For private repositories, GitHub offers 2,000 free minutes per month on public runners, which is enough for most early-stage startups.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;📑 Table of Contents&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🟩 Final Thoughts&lt;/li&gt;
&lt;li&gt;❓ Frequently Asked Questions&lt;/li&gt;
&lt;li&gt;Is GitHub Actions free for startups?&lt;/li&gt;
&lt;li&gt;Can Jenkins run on AWS like GitHub Actions?&lt;/li&gt;
&lt;li&gt;Which tool is more secure for handling AWS credentials?&lt;/li&gt;
&lt;li&gt;📚 References &amp;amp; Further Reading&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Can Jenkins run on AWS like GitHub Actions?
&lt;/h3&gt;

&lt;p&gt;Yes, you can host Jenkins on EC2 or ECS and use auto-scaling groups for agents. However, you're still responsible for backups, security patches, and monitoring, unlike GitHub Actions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Which tool is more secure for handling AWS credentials?
&lt;/h3&gt;

&lt;p&gt;GitHub Actions has stronger default security, with secrets encrypted and scoped to repositories. Jenkins stores credentials in files or the database, which can be vulnerable if the master is compromised.&lt;/p&gt;

&lt;h2&gt;
  
  
  📚 References &amp;amp; Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Official Jenkins Pipeline documentation: &lt;a href="https://www.jenkins.io/doc/book/pipeline/" rel="noopener noreferrer"&gt;jenkins.io&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Managing Jenkins security: &lt;a href="https://www.jenkins.io/doc/book/security/" rel="noopener noreferrer"&gt;jenkins.io&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>tutorial</category>
      <category>cloud</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>🚀 ansible install nginx ubuntu — common mistakes and how to fix them</title>
      <dc:creator>Python-T Point</dc:creator>
      <pubDate>Wed, 06 May 2026 18:34:53 +0000</pubDate>
      <link>https://dev.to/ptp2308/ansible-install-nginx-ubuntu-common-mistakes-and-how-to-fix-them-37ci</link>
      <guid>https://dev.to/ptp2308/ansible-install-nginx-ubuntu-common-mistakes-and-how-to-fix-them-37ci</guid>
      <description>&lt;p&gt;Look — I’ve been there. Late Friday night. Again. Me, staring at three Ubuntu servers, manually editing Nginx configs over SSH because “it’s just a quick change.” Spoiler: it never is. I remember the time I spent two hours debugging why one server wasn’t serving static assets — turned out I’d typoed the root path in &lt;code&gt;/etc/nginx/sites-available/default&lt;/code&gt;. That typo went live at 10 PM. And my manager? Walked in Monday morning, sipped his chai, and said, “You’re still doing this by hand?” Yeah, I learned this the hard way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;📑 Table of Contents&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🚀 Prerequisites — What You &lt;em&gt;Need&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;⚙️ Installing Ansible on Ubuntu&lt;/li&gt;
&lt;li&gt;🔧 Configuring Ansible Hosts&lt;/li&gt;
&lt;li&gt;📦 Creating a Playbook to Install Nginx&lt;/li&gt;
&lt;li&gt;📁 Preparing Files and Running the Playbook&lt;/li&gt;
&lt;li&gt;🧠 Best Practices for Reliable Nginx Deployment&lt;/li&gt;
&lt;li&gt;🔐 Use Variables and Templates&lt;/li&gt;
&lt;li&gt;🔄 Idempotency — Your Best Friend&lt;/li&gt;
&lt;li&gt;🧪 Test in Stages&lt;/li&gt;
&lt;li&gt;🟩 Final Thoughts&lt;/li&gt;
&lt;li&gt;❓ Frequently Asked Questions&lt;/li&gt;
&lt;li&gt;Can I use Ansible to install Nginx on multiple Ubuntu versions?&lt;/li&gt;
&lt;li&gt;Do I need to install Ansible on the target Ubuntu servers?&lt;/li&gt;
&lt;li&gt;How do I handle Nginx configuration reloads without downtime?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That was the day I finally gave Ansible a real shot.&lt;/p&gt;

&lt;p&gt;Honestly, if you're running &lt;strong&gt;Ubuntu&lt;/strong&gt; servers and deploying &lt;strong&gt;Nginx&lt;/strong&gt; , you need Ansible like you need a working kettle in the office pantry. Not just for speed — but for peace of mind. No more “but it worked on my machine.” No more Sunday redeploy scrambles because web3 forgot the config override.&lt;/p&gt;

&lt;p&gt;Let me walk you through how to &lt;strong&gt;ansible install nginx ubuntu&lt;/strong&gt; properly. Step by step. So you don’t have to lose sleep over something that should be automated.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fah9kk1u8tphenskvonb4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fah9kk1u8tphenskvonb4.png" alt="ansible install nginx ubuntu" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 Prerequisites — What You &lt;em&gt;Need&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;Before we dive into installation — here's the thing: tools don’t fix bad setups. I learned this during a migration at a 12-person startup in Bangalore. Team was sharp. Tools were solid. But no SSH keys properly rotated. We spent &lt;em&gt;days&lt;/em&gt; untangling auth chaos.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Control node&lt;/strong&gt; : Your laptop or jump box. Linux preferred. Ubuntu/Debian? Even better. Windows users — use WSL2. Ansible doesn’t run natively on Windows, and wrestling with Cygwin ain’t worth it. (like this one — small, wry, self-aware)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Managed nodes&lt;/strong&gt; : Your Ubuntu boxes. Python 3 installed. SSH access open.&lt;/li&gt;
&lt;li&gt;SSH key-based auth. Non-negotiable. Trust me, password prompts mid-playbook destroy your flow. And your dignity.&lt;/li&gt;
&lt;li&gt;Sudo rights on target machines. Because Nginx needs root, and you’re not running everything as root. Right?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And yeah — skip one of these, and you’ll spend more time debugging than coding.&lt;/p&gt;

&lt;p&gt;It’ll bite you. It always does.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚙️ Installing Ansible on Ubuntu
&lt;/h2&gt;

&lt;p&gt;Alright. Let’s get Ansible on your control machine. This is the brain. The puppet master. The one sending commands across your fleet.&lt;/p&gt;

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

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

&lt;/div&gt;

&lt;p&gt;Get the latest package list. Don’t skip this. Old indices break installs.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt install ansible -y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Done? Good. Now verify:&lt;/p&gt;

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

&lt;/div&gt;

&lt;p&gt;You’re looking for something like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ansible [core 2.14.3]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/youruser/.ansible/plugins/modules', ...]
  ansible python module location = /usr/lib/python3/dist-packages/ansible
  executable location = /usr/bin/ansible
  python version = 3.10.6 (...)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If that shows up — congrats. You’ve just avoided another weekend in crisis mode.&lt;/p&gt;

&lt;p&gt;First real win toward &lt;strong&gt;ansible install nginx ubuntu&lt;/strong&gt; at scale.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔧 Configuring Ansible Hosts
&lt;/h3&gt;

&lt;p&gt;Ansible won’t magically know where your servers live. Gotta tell it.&lt;/p&gt;

&lt;p&gt;Open the default inventory:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo nano /etc/ansible/hosts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Add your nodes. Group them. Makes life easier:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[webservers]
web1 ansible_host=192.168.1.10
web2 ansible_host=192.168.1.11

[webservers:vars]
ansible_user=ubuntu
ansible_ssh_private_key_file=~/.ssh/id_rsa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Save. Exit.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ansible webservers -m ping
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If you get back &lt;code&gt;pong&lt;/code&gt; — you’re golden.&lt;/p&gt;

&lt;p&gt;If not? 9 times out of 10 — it’s SSH keys.&lt;/p&gt;

&lt;p&gt;I once spent &lt;em&gt;two hours&lt;/em&gt; debugging playbook syntax — only to realize I’d copied the public key to &lt;code&gt;web3&lt;/code&gt;’s &lt;code&gt;authorized_keys&lt;/code&gt; but not &lt;code&gt;web2&lt;/code&gt;. Chai break. Realized my mistake. Fixed it in 30 seconds.&lt;/p&gt;

&lt;p&gt;So yeah. Check your keys.&lt;/p&gt;




&lt;h2&gt;
  
  
  📦 Creating a Playbook to Install Nginx
&lt;/h2&gt;

&lt;p&gt;This is where the magic happens.&lt;/p&gt;

&lt;p&gt;Writing a playbook means no more manual steps. No more “I think I ran that command on all servers.”&lt;/p&gt;

&lt;p&gt;Create your project:&lt;/p&gt;

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

&lt;/div&gt;

&lt;p&gt;Make the playbook:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nano site.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now paste this in:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
- name: Install and configure Nginx on Ubuntu
  hosts: webservers
  become: yes
  tasks:
    - name: Update apt cache
      apt:
        update_cache: yes
        cache_valid_time: 3600

    - name: Install Nginx
      apt:
        name: nginx
        state: present

    - name: Ensure Nginx is running and enabled
      systemd:
        name: nginx
        state: started
        enabled: yes

    - name: Copy custom Nginx config
      copy:
        src: files/nginx.conf
        dest: /etc/nginx/nginx.conf
        owner: root
        group: root
        mode: '0644'
      notify: Restart Nginx

  handlers:
    - name: Restart Nginx
      systemd:
        name: nginx
        state: restarted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let’s break it down — quick:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;become: yes&lt;/strong&gt; — runs tasks with sudo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;apt&lt;/strong&gt; module — Ubuntu’s best friend.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;systemd&lt;/strong&gt; — keeps Nginx alive after reboots.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;copy&lt;/strong&gt; task — swaps in your config, if you’ve got one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handlers&lt;/strong&gt; — only run when triggered. Like a restart after config changes. Elegant.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;"Automation isn't about replacing humans — it's about freeing them from the tasks that make them hate Fridays."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  📁 Preparing Files and Running the Playbook
&lt;/h3&gt;

&lt;p&gt;Want a custom &lt;code&gt;nginx.conf&lt;/code&gt;? Create the folder:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir files
nano files/nginx.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Start with the default. Tweak the server name. Add a reverse proxy. Whatever.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ansible-playbook site.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Watch the output.&lt;/p&gt;

&lt;p&gt;Green &lt;code&gt;OK&lt;/code&gt; lines? Good.&lt;/p&gt;

&lt;p&gt;Green &lt;code&gt;CHANGED&lt;/code&gt;? Normal — first run. &lt;em&gt;(More on&lt;a href="https://pythontpoint.in" rel="noopener noreferrer"&gt;PythonTPoint tutorials&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;No red &lt;code&gt;FAILED&lt;/code&gt;? That’s the sound of a dev breathing easy.&lt;/p&gt;

&lt;p&gt;Quick tip — use &lt;code&gt;--check&lt;/code&gt; for dry runs:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ansible-playbook site.yml --check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Shows what &lt;em&gt;would&lt;/em&gt; change. No actual changes. Safe for Fridays. Or when you’ve had three coffees and aren’t sure.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 Best Practices for Reliable Nginx Deployment
&lt;/h2&gt;

&lt;p&gt;Okay. You can &lt;strong&gt;ansible install nginx ubuntu&lt;/strong&gt;. But can you do it &lt;em&gt;well&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;From what I’ve seen on real projects — it’s not about knowing Ansible. It’s about using it right. (Also read: &lt;a href="https://pythontpoint.in/how-to-add-user-to-sudo-group-ubuntu-key-tips-common/" rel="noopener noreferrer"&gt;🐧 How to add user to sudo group ubuntu — key tips &amp;amp; common pitfalls&lt;/a&gt;)&lt;/p&gt;

&lt;h3&gt;
  
  
  🔐 Use Variables and Templates
&lt;/h3&gt;

&lt;p&gt;Hardcoded values? That way lies madness.&lt;/p&gt;

&lt;p&gt;A junior I was mentoring once pushed a playbook with &lt;code&gt;server_name prod-api.somedomain.com&lt;/code&gt; — on a dev server. Spun up Nginx, broke local SSL, took down staging for 10 minutes. Oops.&lt;/p&gt;

&lt;p&gt;So use templates. Jinja2. Variables.&lt;/p&gt;

&lt;p&gt;Make a dir: (Also read: &lt;a href="https://pythontpoint.in/aws-ec2-ssh-permission-denied-fix-ubuntu-common-mistakes/" rel="noopener noreferrer"&gt;🐧 AWS EC2 SSH permission denied fix Ubuntu — common mistakes and how to resolve them&lt;/a&gt;)&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir templates
nano templates/nginx.conf.j2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Add this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;worker_processes {{ ansible_processor_vcpus }};
events {
    worker_connections 1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    server {
        listen      80;
        server_name {{ server_hostname }};
        location / {
            root /var/www/html;
            index index.html;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then update your task:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- name: Deploy templated Nginx config
  template:
    src: templates/nginx.conf.j2
    dest: /etc/nginx/nginx.conf
  notify: Restart Nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Define &lt;code&gt;server_hostname&lt;/code&gt; in vars or inventory. Done. Now it’s reusable.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔄 Idempotency — Your Best Friend
&lt;/h3&gt;

&lt;p&gt;Run the playbook. Run it again.&lt;/p&gt;

&lt;p&gt;If it changes something the second time — you’ve got a problem.&lt;/p&gt;

&lt;p&gt;Idempotency means: same result every time. No surprises.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;apt&lt;/strong&gt; and &lt;strong&gt;systemd&lt;/strong&gt; modules? Designed for this. They check state before acting.&lt;/p&gt;

&lt;p&gt;Always test this. I can’t stress it enough.&lt;/p&gt;

&lt;h3&gt;
  
  
  🧪 Test in Stages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Start with one server — not all five.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;ansible-playbook --step&lt;/code&gt; to go task-by-task. Pause. Verify.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add tags — makes debugging easier:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;name: Install Nginx
apt:
name: nginx
state: present
tags: install&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Then run just that part:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ansible-playbook site.yml --tags install&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Control. Clarity. Calm.&lt;/p&gt;




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

&lt;p&gt;I remember the first time I ran a playbook across ten servers — finished in 90 seconds. All green. No errors. No missing steps.&lt;/p&gt;

&lt;p&gt;No coffee spills. No frantic SSH checks.&lt;/p&gt;

&lt;p&gt;Just consistency.&lt;/p&gt;

&lt;p&gt;That’s when it clicked: this isn’t about Nginx. Or Ansible. It’s about system design. About shifting from firefighter to architect.&lt;/p&gt;

&lt;p&gt;You're not just learning how to &lt;strong&gt;ansible install nginx ubuntu&lt;/strong&gt;. You’re building muscle for scalable, maintainable infrastructure.&lt;/p&gt;

&lt;p&gt;Whether you're a junior pulling late nights or a senior managing clusters — automation like this turns chaos into craft.&lt;/p&gt;

&lt;h2&gt;
  
  
  ❓ Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Can I use Ansible to install Nginx on multiple Ubuntu versions?
&lt;/h3&gt;

&lt;p&gt;Yes. Ansible doesn’t care if it’s 18.04, 20.04, or 22.04 — as long as Python 3 and SSH are there. The &lt;strong&gt;apt&lt;/strong&gt; module works across versions. Just test for compatibility, and use idempotent tasks. From what I’ve seen on real projects, it’s rock solid.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need to install Ansible on the target Ubuntu servers?
&lt;/h3&gt;

&lt;p&gt;Nope. That’s the beauty of it. Ansible is agentless. Runs from the control node. Target servers only need Python and SSH. Makes adoption smooth. Trust me, I’ve used Puppet before — this is &lt;em&gt;so&lt;/em&gt; much lighter.&lt;/p&gt;

</description>
      <category>ansible</category>
      <category>devops</category>
      <category>automation</category>
      <category>linux</category>
    </item>
    <item>
      <title>🐍 python multiple inheritance examples — common mistakes and how to fix them</title>
      <dc:creator>Python-T Point</dc:creator>
      <pubDate>Wed, 06 May 2026 03:44:25 +0000</pubDate>
      <link>https://dev.to/ptp2308/python-multiple-inheritance-examples-common-mistakes-and-how-to-fix-them-39bj</link>
      <guid>https://dev.to/ptp2308/python-multiple-inheritance-examples-common-mistakes-and-how-to-fix-them-39bj</guid>
      <description>&lt;p&gt;The bug took six hours of tracing through middleware layers. The fix came down to understanding how Python resolves method calls in multiple inheritance using the &lt;strong&gt;Method Resolution Order (MRO)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We’d built a monitoring system that combined logging, alerting, and rate-limiting behaviors into service classes. Everything worked—until two base classes defined the same method. Suddenly, alerts weren’t being sent, but no exception was raised. No crash. Just silence from a critical service.&lt;/p&gt;

&lt;p&gt;This post explains what went wrong and how to avoid it. Multiple inheritance in Python isn’t magic. It’s deterministic. When you understand MRO, you can use multiple inheritance safely—and know exactly when to reach for mixins instead.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy859qgagd2ip1lpkq5ly.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy859qgagd2ip1lpkq5ly.png" alt="python multiple inheritance examples" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 Understanding MRO — How Python &lt;em&gt;Resolves&lt;/em&gt; Method Calls
&lt;/h2&gt;

&lt;p&gt;Python uses the &lt;strong&gt;C3 linearization algorithm&lt;/strong&gt; to compute a consistent Method Resolution Order (MRO). This isn’t left-to-right in the class list—it respects inheritance hierarchy and ensures monotonicity.&lt;/p&gt;

&lt;p&gt;Every class has an &lt;code&gt;.mro()&lt;/code&gt; method that returns the resolution order. When you call &lt;code&gt;obj.method()&lt;/code&gt;, Python walks this list left to right and stops at the first class that defines the method.&lt;/p&gt;

&lt;p&gt;Here’s an example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class A:
    def process(self):
        print("A.process")

class B(A):
    def process(self):
        print("B.process")

class C(A):
    def process(self):
        print("C.process")

class D(B, C):
    pass

d = D()
d.process()  # What gets printed?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You might assume &lt;code&gt;B.process&lt;/code&gt; because &lt;code&gt;B&lt;/code&gt; appears first. Let’s check:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;print(D.mro())



[&amp;lt;class '__main__.D'&amp;gt;, &amp;lt;class '__main__.B'&amp;gt;, &amp;lt;class '__main__.C'&amp;gt;, &amp;lt;class '__main__.A'&amp;gt;, &amp;lt;class 'object'&amp;gt;]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;So yes, &lt;code&gt;d.process()&lt;/code&gt; calls &lt;code&gt;B.process&lt;/code&gt;. But swap the scenario: remove &lt;code&gt;process&lt;/code&gt; from &lt;code&gt;B&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class B(A):
    pass  # no process
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now &lt;code&gt;D(B, C)&lt;/code&gt; calls &lt;code&gt;C.process&lt;/code&gt;. The MRO hasn’t changed—&lt;code&gt;[D, B, C, A]&lt;/code&gt;—but since &lt;code&gt;B&lt;/code&gt; doesn’t define &lt;code&gt;process&lt;/code&gt;, resolution continues to &lt;code&gt;C&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The mechanism: &lt;strong&gt;method lookup walks the MRO until it finds an implementation&lt;/strong&gt;. There’s no fallback logic, no ambiguity. It just follows the list.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Multiple inheritance isn’t dangerous if you know the MRO. It’s dangerous if you don’t check it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  🔧 MRO Gotcha: The Diamond Problem
&lt;/h3&gt;

&lt;p&gt;This occurs when two parent classes inherit from the same grandparent. Without C3, you could call the grandparent method twice. Python’s MRO prevents duplication.&lt;/p&gt;

&lt;p&gt;In our example, &lt;code&gt;A&lt;/code&gt; appears only once in the MRO—even though both &lt;code&gt;B&lt;/code&gt; and &lt;code&gt;C&lt;/code&gt; inherit from it. C3 guarantees each class appears just once and in an order that respects the hierarchy.&lt;/p&gt;

&lt;h3&gt;
  
  
  💻 Visualizing MRO with super()
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;super()&lt;/code&gt; doesn’t mean “the parent.” It means “the next class in the MRO.” That distinction matters.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class A:
    def process(self):
        print("A.process")

class B(A):
    def process(self):
        print("B.process")
        super().process()

class C(A):
    def process(self):
        print("C.process")
        super().process()

class D(B, C):
    def process(self):
        print("D.process")
        super().process()

D().process()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Output:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;D.process
B.process
C.process
A.process
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Each &lt;code&gt;super()&lt;/code&gt; call advances along the MRO: &lt;code&gt;D → B → C → A&lt;/code&gt;. The chain is predictable because it’s defined by the class structure.&lt;/p&gt;

&lt;h3&gt;
  
  
  ⚙️ When MRO Fails: Inconsistent Hierarchies
&lt;/h3&gt;

&lt;p&gt;If C3 can’t produce a valid order, Python raises a &lt;code&gt;TypeError&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class X: pass
class Y: pass
class A(X, Y): pass
class B(Y, X): pass
class C(A, B): pass  # ❌ TypeError: cannot create a consistent method resolution
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Output:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TypeError: Cannot create a consistent method resolution order (MRO) for bases A, B
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The conflict arises because &lt;code&gt;A&lt;/code&gt; requires &lt;code&gt;X&lt;/code&gt; before &lt;code&gt;Y&lt;/code&gt;, but &lt;code&gt;B&lt;/code&gt; requires the reverse. C3 detects the contradiction and refuses to proceed. This fails fast—better than silent misbehavior.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧩 Mixins — Reusable &lt;em&gt;Behaviors&lt;/em&gt; Without Deep Inheritance
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;mixin&lt;/strong&gt; is a class that adds specific functionality to others. It doesn’t stand alone. It models “can-do,” not “is-a.”&lt;/p&gt;

&lt;p&gt;Common uses include logging, caching, serialization, or permissions. The key is that they’re state-light and designed to chain via &lt;code&gt;super()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here’s a practical example: an API client that logs, caches, and rate-limits requests.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class LoggingMixin:
    def log(self, message):
        print(f"[LOG] {message}")

    def request(self, url):
        self.log(f"Requesting {url}")
        return super().request(url)

class CachingMixin:
    def __init__(self):
        self._cache = {}
        super().__init__()

    def request(self, url):
        if url in self._cache:
            return self._cache[url]
        response = super().request(url)
        self._cache[url] = response
        return response

class RateLimitMixin:
    def __init__(self):
        from time import time
        self._last_call = 0
        self._rate_limit = 1  # seconds
        super().__init__()

    def request(self, url):
        import time
        now = time.time()
        if now - self._last_call &amp;lt; self._rate_limit:
            time.sleep(self._rate_limit - (now - self._last_call))
        self._last_call = now
        return super().request(url)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now compose them:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class HTTPClient:
    def request(self, url):
        return f"Response from {url}"

class SmartClient(LoggingMixin, CachingMixin, RateLimitMixin, HTTPClient):
    pass
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Inspect the MRO:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;print(SmartClient.mro())



[&amp;lt;class '__main__.SmartClient'&amp;gt;,
 &amp;lt;class '__main__.LoggingMixin'&amp;gt;,
 &amp;lt;class '__main__.CachingMixin'&amp;gt;,
 &amp;lt;class '__main__.RateLimitMixin'&amp;gt;,
 &amp;lt;class '__main__.HTTPClient'&amp;gt;,
 &amp;lt;class 'object'&amp;gt;]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Call the client:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;client = SmartClient()
print(client.request("https://api.example.com/data"))
print(client.request("https://api.example.com/data"))  # cached
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Output:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[LOG] Requesting https://api.example.com/data
Response from https://api.example.com/data
[LOG] Requesting https://api.example.com/data
Response from https://api.example.com/data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The second call still logs and checks rate limits—but hits the cache. The behavior chain runs in MRO order, each &lt;code&gt;super()&lt;/code&gt; advancing to the next.&lt;/p&gt;

&lt;p&gt;This is a valid use of multiple inheritance. Each mixin adds one concern. The MRO is clear. And &lt;code&gt;super()&lt;/code&gt; chains work as intended.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔧 Mixin Design Rules
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Always call &lt;code&gt;super().**init**()&lt;/code&gt; in &lt;code&gt;**init**&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;Assume required methods exist in classes later in the MRO.
&lt;/li&gt;
&lt;li&gt;Keep mixins focused—only one behavior per mixin.
&lt;/li&gt;
&lt;li&gt;Use the &lt;code&gt;Mixin&lt;/code&gt; suffix for clarity.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  💡 When Not to Use Mixins
&lt;/h3&gt;

&lt;p&gt;Avoid mixins when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The base class doesn’t support &lt;code&gt;super()&lt;/code&gt; chaining.
&lt;/li&gt;
&lt;li&gt;Two mixins define conflicting state (e.g., both use &lt;code&gt;_data&lt;/code&gt;).
&lt;/li&gt;
&lt;li&gt;You need multiple methods to run unconditionally—mixins override, not combine.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📦 Composition vs. Inheritance — Choosing the &lt;em&gt;Right&lt;/em&gt; Tool
&lt;/h2&gt;

&lt;p&gt;Inheritance implies “is-a.” Composition implies “has-a.” Composition often wins for clarity and testability.&lt;/p&gt;

&lt;p&gt;Here’s the same functionality using composition:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Logger:
    def log(self, message):
        print(f"[LOG] {message}")

class Cache:
    def __init__(self):
        self._cache = {}
    def get(self, key, fetch_fn):
        if key not in self._cache:
            self._cache[key] = fetch_fn()
        return self._cache[key]

class RateLimiter:
    def __init__(self, rate_limit=1):
        self._last_call = 0
        self._rate_limit = rate_limit
    def limit(self):
        import time
        now = time.time()
        if now - self._last_call &amp;lt; self._rate_limit:
            time.sleep(self._rate_limit - (now - self._last_call))
        self._last_call = now

class HTTPClient:
    def __init__(self):
        self.logger = Logger()
        self.cache = Cache()
        self.rate_limiter = RateLimiter(rate_limit=1)

    def request(self, url):
        self.logger.log(f"Requesting {url}")
        self.rate_limiter.limit()
        return self.cache.get(url, lambda: f"Response from {url}")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;No MRO. No &lt;code&gt;super()&lt;/code&gt;. Just direct method calls. Easier to debug. Easier to test. More control.&lt;/p&gt;

&lt;p&gt;Use &lt;strong&gt;multiple inheritance&lt;/strong&gt; when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You’re applying cross-cutting behaviors via well-designed mixins.
&lt;/li&gt;
&lt;li&gt;You’re extending frameworks that expect it (e.g., Django views).
&lt;/li&gt;
&lt;li&gt;The method chain is intentional and &lt;code&gt;super()&lt;/code&gt; is used consistently.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use &lt;strong&gt;composition&lt;/strong&gt; when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Execution order must be explicit.
&lt;/li&gt;
&lt;li&gt;You’re combining stateful components.
&lt;/li&gt;
&lt;li&gt;The MRO feels like a liability.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🧠 Mechanism: super() is Dynamic
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;super()&lt;/code&gt; doesn’t hardcode the next class. It uses the MRO of the &lt;strong&gt;instance’s class&lt;/strong&gt; , not the class where the method is defined.&lt;/p&gt;

&lt;p&gt;That means &lt;code&gt;LoggingMixin&lt;/code&gt; behaves correctly in different inheritance contexts—because &lt;code&gt;super()&lt;/code&gt; always follows the actual MRO at runtime.&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 Real-World Example — Django Forms and Mixins
&lt;/h2&gt;

&lt;p&gt;Django’s class-based views rely heavily on multiple inheritance and mixins.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import CreateView
from myapp.models import Task

class TaskCreateView(LoginRequiredMixin, CreateView):
    model = Task
    fields = ['title', 'due_date']
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;LoginRequiredMixin.dispatch&lt;/code&gt; runs first. It checks authentication, then delegates to &lt;code&gt;super().dispatch()&lt;/code&gt;, which eventually reaches &lt;code&gt;CreateView.dispatch&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The MRO ensures the security check runs before any view logic.&lt;/p&gt;

&lt;p&gt;Debug it:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;print(TaskCreateView.mro())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Output:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[&amp;lt;class 'myapp.views.TaskCreateView'&amp;gt;,
 &amp;lt;class 'django.contrib.auth.mixins.LoginRequiredMixin'&amp;gt;,
 &amp;lt;class 'django.views.generic.edit.CreateView'&amp;gt;,
 ...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;LoginRequiredMixin&lt;/code&gt; appears first in the MRO—so its &lt;code&gt;dispatch&lt;/code&gt; takes precedence.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔧 Debugging MRO in Django
&lt;/h3&gt;

&lt;p&gt;If a mixin seems to be ignored, run &lt;code&gt;.mro()&lt;/code&gt; and verify its position. If it’s after the target class, it won’t intercept the method.&lt;/p&gt;

&lt;h3&gt;
  
  
  💡 Avoiding Conflicts
&lt;/h3&gt;

&lt;p&gt;Never define &lt;code&gt;form_valid()&lt;/code&gt; in two mixins unless you intend one to override the other. If you need multiple behaviors on form save, use signals or refactor to composition.&lt;/p&gt;




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

&lt;p&gt;Multiple inheritance is a tool, not a trap. The issue isn’t the feature—it’s using it without understanding the MRO.&lt;/p&gt;

&lt;p&gt;I’ve seen teams ban it after one bug. That’s overcorrection. Learn how it works. Check &lt;code&gt;YourClass.mro()&lt;/code&gt; early and often. Use it with mixins that are designed for chaining.&lt;/p&gt;

&lt;p&gt;More than that: understanding MRO makes you a better reader of code. You’ll predict behavior. You’ll debug faster. You’ll design systems that are both flexible and maintainable.&lt;/p&gt;

&lt;p&gt;Just run &lt;code&gt;print(YourClass.mro())&lt;/code&gt;. Five seconds. Prevents six hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  ❓ Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Can I use multiple inheritance with &lt;code&gt;**init**&lt;/code&gt; methods?
&lt;/h3&gt;

&lt;p&gt;Yes, but only if all classes use &lt;strong&gt;super()&lt;/strong&gt; consistently. Direct calls like &lt;code&gt;Parent.__init__(self)&lt;/code&gt; break the chain and can cause skipped or duplicated calls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;📑 Table of Contents&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🧠 Understanding MRO — How Python &lt;em&gt;Resolves&lt;/em&gt; Method Calls&lt;/li&gt;
&lt;li&gt;🔧 MRO Gotcha: The Diamond Problem&lt;/li&gt;
&lt;li&gt;💻 Visualizing MRO with super()&lt;/li&gt;
&lt;li&gt;⚙️ When MRO Fails: Inconsistent Hierarchies&lt;/li&gt;
&lt;li&gt;🧩 Mixins — Reusable &lt;em&gt;Behaviors&lt;/em&gt; Without Deep Inheritance&lt;/li&gt;
&lt;li&gt;🔧 Mixin Design Rules&lt;/li&gt;
&lt;li&gt;💡 When Not to Use Mixins&lt;/li&gt;
&lt;li&gt;📦 Composition vs. Inheritance — Choosing the &lt;em&gt;Right&lt;/em&gt; Tool&lt;/li&gt;
&lt;li&gt;🧠 Mechanism: super() is Dynamic&lt;/li&gt;
&lt;li&gt;🚀 Real-World Example — Django Forms and Mixins&lt;/li&gt;
&lt;li&gt;🔧 Debugging MRO in Django&lt;/li&gt;
&lt;li&gt;💡 Avoiding Conflicts&lt;/li&gt;
&lt;li&gt;🟩 Final Thoughts&lt;/li&gt;
&lt;li&gt;❓ Frequently Asked Questions&lt;/li&gt;
&lt;li&gt;Can I use multiple inheritance with &lt;code&gt;**init**&lt;/code&gt; methods?&lt;/li&gt;
&lt;li&gt;What happens if two parent classes define the same method?&lt;/li&gt;
&lt;li&gt;Are mixins the same as abstract base classes?&lt;/li&gt;
&lt;li&gt;📚 References &amp;amp; Further Reading&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What happens if two parent classes define the same method?
&lt;/h3&gt;

&lt;p&gt;Python uses the &lt;strong&gt;MRO&lt;/strong&gt; to decide. The first class in the list that defines the method is used. You can inspect the order with &lt;code&gt;ClassName.mro()&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Are mixins the same as abstract base classes?
&lt;/h3&gt;

&lt;p&gt;No. &lt;strong&gt;Mixins&lt;/strong&gt; provide reusable implementation. &lt;strong&gt;Abstract base classes&lt;/strong&gt; define interfaces and enforce method contracts. A class can use both for different purposes.&lt;/p&gt;

&lt;h2&gt;
  
  
  📚 References &amp;amp; Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Official Python docs on multiple inheritance and MRO: &lt;a href="https://docs.python.org/3/tutorial/classes.html#multiple-inheritance" rel="noopener noreferrer"&gt;docs.python.org&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Django class-based views and mixin patterns: &lt;a href="https://docs.djangoproject.com/en/stable/topics/class-based-views/" rel="noopener noreferrer"&gt;docs.djangoproject.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Python data model and super() behavior: &lt;a href="https://docs.python.org/3/reference/datamodel.html#invoking-descriptors" rel="noopener noreferrer"&gt;docs.python.org&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
