<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Ravisingh Chauhan Blogs]]></title><description><![CDATA[Ravisingh Chauhan Blogs]]></description><link>https://ravisingh88.hashnode.dev</link><image><url>https://cdn.hashnode.com/uploads/logos/6a1dbe42cc268013977157f8/9bdcbee3-089b-4cb9-b382-6cce02ba209a.png</url><title>Ravisingh Chauhan Blogs</title><link>https://ravisingh88.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Mon, 02 Nov 2026 21:55:41 GMT</lastBuildDate><atom:link href="https://ravisingh88.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How I Learned Docker Networking by Trying to Connect a Container to My Host MySQL]]></title><description><![CDATA[This is my first article — and honestly I'm writing it because I spent way too long debugging something that looked simple on the surface. Figured if I…]]></description><link>https://ravisingh88.hashnode.dev/how-i-learned-docker-networking-by-trying-to-connect-a-container-to-my-host-mysql</link><guid isPermaLink="true">https://ravisingh88.hashnode.dev/how-i-learned-docker-networking-by-trying-to-connect-a-container-to-my-host-mysql</guid><dc:creator><![CDATA[Ravisingh Chauhan]]></dc:creator><pubDate>Tue, 02 Jun 2026 05:13:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a1dbe42cc268013977157f8/9ecb4ab9-d752-4f8b-acb4-9957ddda756a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is my first article — and honestly I'm writing it because I spent way too long debugging something that looked simple on the surface. Figured if I struggled with it, someone else probably will too.</p>
<p>The goal was straightforward: run a MySQL client inside a Docker container and connect it to the MySQL database running on my local machine (the host). No docker-compose, no fancy setup. Just a container talking to my laptop's database.</p>
<p>What followed was a few hours of <code>connection refused</code>, some googling, and eventually understanding how Docker networking actually works under the hood.</p>
<p>Let me walk you through what I learned.</p>
<hr />
<h2>The Problem — Why 127.0.0.1 Doesn't Work Inside a Container</h2>
<p>My first instinct was just to connect to <code>127.0.0.1</code>. That's localhost, right? MySQL is running there.</p>
<pre><code class="language-bash">mysql -h 127.0.0.1 -u root -p
# ERROR 2003 (HY000): Can't connect to MySQL server on '127.0.0.1'
</code></pre>
<p>Nope.</p>
<p>Here's the thing — inside a container, <code>127.0.0.1</code> is the <strong>container's own loopback</strong>. Not your machine. Your host machine doesn't exist at that address from inside the container. The container is living in its own isolated network. Your laptop is somewhere else entirely.</p>
<p>So where is your host machine from the container's perspective? That's where <code>docker0</code> comes in.</p>
<hr />
<h2>What is docker0?</h2>
<p>When you install Docker, it automatically creates a virtual network interface on your machine called <strong>docker0</strong>.</p>
<p>Run this on your host and you'll see it:</p>
<pre><code class="language-bash">ip addr show docker0
# 3: docker0: inet 172.17.0.1/24
</code></pre>
<p>Think of <code>docker0</code> as a <strong>virtual network switch</strong> that Docker manages. It has its own IP — usually <code>172.17.0.1</code> — and every container you run connects to it.</p>
<pre><code class="language-plaintext">Your Machine
├── eth0        → your real network card (connects to internet/router)
└── docker0     → virtual bridge (172.17.0.1)
      ├── Container A (172.17.0.2)
      ├── Container B (172.17.0.3)
      └── Container C (172.17.0.4)
</code></pre>
<p>Each container gets connected to <code>docker0</code> through something called a <strong>veth pair</strong> — a virtual ethernet cable. One end is inside the container (shows up as <code>eth0</code> inside), the other end is plugged into <code>docker0</code> on the host.</p>
<p>So from inside a container, your <strong>host machine is reachable at</strong> <code>172.17.0.1</code> — the docker0 IP.</p>
<hr />
<h2>Fix #1 — Change MySQL's Bind Address</h2>
<p>Even once I knew the right IP, I still got refused. Because MySQL on my machine was configured to only accept connections from <code>127.0.0.1</code>.</p>
<p>Check your MySQL config:</p>
<pre><code class="language-bash"># usually at /etc/mysql/mysql.conf.d/mysqld.cnf
grep bind-address /etc/mysql/mysql.conf.d/mysqld.cnf
# bind-address = 127.0.0.1
</code></pre>
<p>That <code>127.0.0.1</code> means MySQL is only listening on localhost — it completely ignores any connection coming from the container subnet.</p>
<p>Change it to <code>0.0.0.0</code> so it listens on all interfaces:</p>
<pre><code class="language-ini">bind-address = 0.0.0.0
</code></pre>
<p>Then restart MySQL:</p>
<pre><code class="language-bash">sudo systemctl restart mysql
</code></pre>
<hr />
<h2>Fix #2 — Create a MySQL User for Docker</h2>
<p>I didn't want to use <code>root</code> for this, so I created a dedicated MySQL user that's allowed to connect from the container subnet (<code>172.17.0.%</code>).</p>
<p>Log into MySQL on your host:</p>
<pre><code class="language-sql">CREATE USER 'dockeruser'@'172.17.0.%' IDENTIFIED BY 'yourpassword';
GRANT ALL PRIVILEGES ON yourdb.* TO 'dockeruser'@'172.17.0.%';
FLUSH PRIVILEGES;
</code></pre>
<p>The <code>172.17.0.%</code> means this user can connect from any IP in the container subnet — not just from localhost.</p>
<hr />
<h2>Fix #3 — Allow the Port Through the Firewall</h2>
<p>One more thing I missed — my firewall was blocking port 3306 from the container network.</p>
<pre><code class="language-bash">sudo ufw allow from 172.17.0.0/24 to any port 3306
</code></pre>
<p>Now from inside the container:</p>
<pre><code class="language-bash">mysql -h 172.17.0.2 -u dockeruser -p
# Welcome to the MySQL monitor. 
</code></pre>
<p>Finally. It works.</p>
<hr />
<h2>But Wait — How Does the Internet Work from Inside a Container?</h2>
<p>While I was debugging all this, I got curious about something else. If containers have these private IPs like <code>172.17.0.3</code> — how does <code>curl google.com</code> even work inside a container? Private IPs aren't routable on the internet. Google can't send a reply back to <code>172.17.0.3</code>.</p>
<p>Let's trace what actually happens when you run something like:</p>
<pre><code class="language-bash"># inside a container
curl https://google.com
</code></pre>
<p><strong>Step 1 — DNS</strong></p>
<p>The container resolves <code>google.com</code> using Docker's embedded DNS resolver (<code>127.0.0.11</code>), which forwards to your host's DNS and returns Google's real IP.</p>
<p><strong>Step 2 — Packet leaves the container</strong></p>
<p>The container sends a packet:</p>
<ul>
<li><p>Source IP: <code>172.17.0.3</code> (the container)</p>
</li>
<li><p>Destination IP: <code>142.250.x.x</code> (Google)</p>
</li>
</ul>
<p>This travels through the veth pair → hits <code>docker0</code>.</p>
<p><strong>Step 3 — Linux kernel routing</strong></p>
<p>The host kernel looks at its routing table:</p>
<pre><code class="language-bash">ip route
# default via 192.168.1.1 dev eth0    → internet goes here
# 172.17.0.0/24 dev docker0           → containers live here
</code></pre>
<p>Google's IP isn't in the container subnet, so the kernel forwards it out through <code>eth0</code>.</p>
<p><strong>Step 4 — SNAT / Masquerade</strong></p>
<p>Here's the key part. Before the packet leaves your machine, Linux rewrites the source IP. It replaces <code>172.17.0.3</code> with your machine's actual IP (<code>192.168.1.x</code>).</p>
<p>This is called <strong>SNAT (Source NAT)</strong> or <strong>Masquerade</strong>. Docker sets this up automatically using iptables:</p>
<pre><code class="language-bash">sudo iptables -t nat -L POSTROUTING -n
# MASQUERADE all -- 172.17.0.0/24 !172.17.0.0/24
</code></pre>
<p>So the packet that hits Google's servers has your real machine IP as the source. Google replies to your machine. Then Linux uses <strong>conntrack</strong> (connection tracking) to remember which container made the request, and forwards the reply back to <code>172.17.0.3</code>.</p>
<pre><code class="language-plaintext">Container sends:          After Masquerade:        Google sees:
SRC: 172.17.0.3    →      SRC: 192.168.1.10   →   your machine's IP
DST: 142.250.x.x          DST: 142.250.x.x
</code></pre>
<p>The container gets its response and has no idea any of this happened. It's completely transparent.</p>
<p>This is the same reason your home router works — your router does the same NAT trick so multiple devices can share one public IP.</p>
<hr />
<h2>The Full Picture</h2>
<pre><code class="language-plaintext">Your Machine
┌───────────────────────────────────────────────────────┐
│                                                       │
│  Container (172.17.0.3)                               │
│    └── veth ──► docker0 (172.17.0.1)                  │
│                    └── iptables (MASQUERADE/SNAT)     │
│                           └── eth0 ──► Router ──► Internet
│                                                       │
│  Host MySQL listening on 0.0.0.0:3306                 │
│    └── reachable from container at 172.17.0.1:3306    │
│                                                       │
└───────────────────────────────────────────────────────┘
</code></pre>
<hr />
<h2>Quick Reference</h2>
<table>
<thead>
<tr>
<th>What you want</th>
<th>How to do it</th>
</tr>
</thead>
<tbody><tr>
<td>Container → Host MySQL</td>
<td>Connect to <code>172.17.0.1</code> or <code>host.docker.internal</code></td>
</tr>
<tr>
<td>MySQL accepting container connections</td>
<td>Set <code>bind-address = 0.0.0.0</code></td>
</tr>
<tr>
<td>MySQL user for containers</td>
<td>Create user with <code>@'172.17.0.%'</code></td>
</tr>
<tr>
<td>Container → Internet</td>
<td>Works automatically (SNAT/Masquerade)</td>
</tr>
<tr>
<td>No isolation needed (dev only)</td>
<td><code>docker run --network=host</code></td>
</tr>
</tbody></table>
<hr />
<h2>What I Took Away From This</h2>
<p>Before this I thought Docker networking was just magic. Now I understand there's actual Linux networking underneath — bridges, routing, NAT, connection tracking. Docker just sets it all up for you automatically.</p>
<p>The concepts here — veth pairs, bridge networking, SNAT — are also the foundation of how Kubernetes networking works. So if you're planning to get into K8s eventually, understanding this stuff first really helps.</p>
<p>If you're just getting started with Docker, try this exercise yourself. Deliberately break things, read the error messages, and trace what's actually happening. You learn way more than just following a tutorial.</p>
<hr />
<p>If anything here was confusing or wrong, drop a comment — I'm still learning this stuff too.</p>
]]></content:encoded></item></channel></rss>