<?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: Thomas Plat</title>
    <description>The latest articles on DEV Community by Thomas Plat (@thpl).</description>
    <link>https://dev.to/thpl</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%2F3965848%2Fb056b44e-053e-4a2d-93c3-dea5c162dd61.jpg</url>
      <title>DEV Community: Thomas Plat</title>
      <link>https://dev.to/thpl</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/thpl"/>
    <language>en</language>
    <item>
      <title>The Container Port Binding Mistake That Breaks Almost Every First Deploy</title>
      <dc:creator>Thomas Plat</dc:creator>
      <pubDate>Wed, 03 Jun 2026 06:40:17 +0000</pubDate>
      <link>https://dev.to/thpl/the-container-port-binding-mistake-that-breaks-almost-every-first-deploy-3fng</link>
      <guid>https://dev.to/thpl/the-container-port-binding-mistake-that-breaks-almost-every-first-deploy-3fng</guid>
      <description>&lt;p&gt;You deploy your app. The build succeeds. The logs show the server starting. You click the URL your deployment platform gave you and get a connection error, a 502, or nothing at all.&lt;/p&gt;

&lt;p&gt;This is one of the most common first deployment failures, and the cause is almost always the same: the app is binding to the wrong address, or listening on the wrong port, or both.&lt;/p&gt;

&lt;h2&gt;
  
  
  What port binding actually means
&lt;/h2&gt;

&lt;p&gt;When a server app starts, it listens for incoming connections on a network address. That address has two parts: the IP address it listens on, and the port number.&lt;/p&gt;

&lt;p&gt;The IP address determines which network interfaces the application accepts connections from. &lt;code&gt;localhost&lt;/code&gt; (which resolves to &lt;code&gt;127.0.0.1&lt;/code&gt;) means the app only accepts connections from the same machine. &lt;code&gt;0.0.0.0&lt;/code&gt; means the app accepts connections from any network interface, including external ones.&lt;/p&gt;

&lt;p&gt;During local development, &lt;code&gt;localhost&lt;/code&gt; is fine. Everything is on the same machine. Your browser and your server are both on your laptop. When you deploy to a server, the platform's load balancer is not on the same machine as your app. It is trying to connect from outside. An app bound to &lt;code&gt;localhost&lt;/code&gt; is invisible to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The port problem
&lt;/h2&gt;

&lt;p&gt;Deployment platforms often assign ports dynamically. They tell your app which port to use through an environment variable, almost always called &lt;code&gt;PORT&lt;/code&gt;. Your app needs to read this variable and bind to that port.&lt;/p&gt;

&lt;p&gt;If your app ignores &lt;code&gt;PORT&lt;/code&gt; and hardcodes a port number, it starts on a port the platform is not watching. The platform tries to connect on its assigned port, gets nothing, and marks the deployment as failed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This will fail on most platforms&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// This is correct&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;|| 3000&lt;/code&gt; fallback makes the app work both locally (where &lt;code&gt;PORT&lt;/code&gt; is not set) and in production (where the platform sets it).&lt;/p&gt;

&lt;h2&gt;
  
  
  What AI tools generate
&lt;/h2&gt;

&lt;p&gt;AI tools often hardcode both the address and the port. The generated code looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Server running on http://localhost:3000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is correct for local development and wrong for production. The &lt;code&gt;'localhost'&lt;/code&gt; argument is the binding address. Remove it entirely or replace it with &lt;code&gt;'0.0.0.0'&lt;/code&gt;. Replace &lt;code&gt;3000&lt;/code&gt; with &lt;code&gt;process.env.PORT&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The same pattern appears across multiple frameworks. Express does it. Fastify does it. Hapi does it. The underlying behavior is the same in all of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Framework-specific fixes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Express:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0.0.0.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fastify:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fastify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0.0.0.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;AdonisJS:&lt;/strong&gt; Set &lt;code&gt;HOST=0.0.0.0&lt;/code&gt; and &lt;code&gt;PORT&lt;/code&gt; in your environment. AdonisJS reads both from environment variables automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next.js:&lt;/strong&gt; Next.js handles port binding correctly by default and reads &lt;code&gt;PORT&lt;/code&gt; from the environment. No manual fix needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NestJS:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0.0.0.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to diagnose it
&lt;/h2&gt;

&lt;p&gt;If your deployment shows the application is starting but the health check is failing, check two things in the startup logs:&lt;/p&gt;

&lt;p&gt;First, what address is the server logging? If you see &lt;code&gt;Listening on http://localhost:3000&lt;/code&gt; or &lt;code&gt;Server running on 127.0.0.1:3000&lt;/code&gt;, the app is bound to localhost. External traffic cannot reach it.&lt;/p&gt;

&lt;p&gt;Second, what port is the app using? If it is hardcoded and does not match the &lt;code&gt;PORT&lt;/code&gt; environment variable, the platform is sending traffic to the wrong port.&lt;/p&gt;

&lt;p&gt;Both of these are visible in the startup log lines that most frameworks print when they start successfully.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is so consistent
&lt;/h2&gt;

&lt;p&gt;This failure is nearly universal for first deployments because it is invisible during development. The hardcoded localhost binding works perfectly when you are testing locally. Nothing ever fails. The code ships, the app starts on the server, and the binding address becomes a problem for the first time.&lt;/p&gt;

&lt;p&gt;Catching this before deployment is one of the more valuable things a deployment platform can do automatically. jetpacked.ai detects hardcoded port and address bindings during repo analysis — apps that don't listen on 0.0.0.0 or ignore PORT won't serve traffic, and surfacing that before the build starts saves the debugging loop entirely.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
