Learning paths / Vibe coding to production / Already built it? Start here

Why it works locally and not online

Reading · 5 min · Module 2, lesson 3 of 410 min left in this module

Module 2 · Already built it? Start hereLesson 3 of 4

Goal: Find and fix a hard-coded listen address and port, so the starter app can be reached once it's deployed.

3:55 · captions and chapters · narrated with an AI-generated voice
Transcript

Narration uses an AI-generated voice.

[00:00] Where we're going

By the end of this video, you'll find the one line that stops the starter answering online, and you'll fix it. It's the most common reason an app an agent built works on your laptop, and fails once it's deployed.

[00:14] Why the agent wrote it

Agents write code that works where they tested it. And that's your laptop. Here's the starter, running the way the agent ran it. Then it checks the health endpoint, from the same machine. And it gets an answer. So the loop's observe step saw it work. Nothing it ran looked like the internet. Tutorials and dev servers do the same. They default to localhost, because on a laptop, that's the safe choice.

Online, it's different. Requests arrive from outside the machine. And one twenty-seven dot zero dot zero dot one only accepts connections from the same machine. So the app has to listen on zero dot zero dot zero dot zero. That's every address the machine has. And on the port the platform expects. Lesson one point four point three explains why, in detail.

[01:11] Find it in the starter

So let's find it. Open server dot js, and look at the last lines. Two things are hard-coded. First, the address. The loopback, one twenty-seven dot zero dot zero dot one. Second, the port. Three thousand. On your laptop, both are fine. That's exactly why this bug survives until the first deploy.

[01:35] Fix it

Now the fix. Replace those lines with these. First, it reads the port from the PORT environment variable. When that isn't set, it falls back to three thousand. Then it listens on zero dot zero dot zero dot zero. Every address, not just the loopback.

[01:55] Prove it

Now prove both halves on your machine. Start the app with PORT set to four thousand. The log says it's listening on port four thousand. In another terminal, check the health endpoint on four thousand. It answers, status okay. And localhost still works, because every address includes it. Then stop it, and run the tests, to check nothing else broke. All seven pass. So commit the fix, and push it.

[02:24] Ask the right question

In your own app, the same bug can hide somewhere else. In a framework's start command, or a config file. Not always in a listen call. So ask your agent a question, not for a fix. Where does this app choose its listen address and port? Show me the lines. Read the answer. Then decide the change yourself.

[02:49] On ComputeSphere

On ComputeSphere, a web service's Port is where traffic is sent. It has to match the port your app listens on. Here, that's three thousand, unless you set PORT. If they differ, or the app only listens on the loopback, the deploy never reaches Running. It ends Failed.

[03:09] Recap

To recap. Agents write code that works where they tested it. So listen on zero dot zero dot zero dot zero, and on the port in PORT. Prove it on your machine, with a different port. And in your own app, ask where the address and port are chosen. Then decide yourself.

Here's a question to check yourself. The starter works at localhost, port three thousand. Deployed as it is, why would it fail? Because it only listens on one twenty-seven dot zero dot zero dot one, and the platform's traffic arrives from outside. Next, check what you've learned. I'll see you there.

Key idea

Agents write code that works where they tested it: your laptop. The most common reason it then fails online is one line. The app listens on 127.0.0.1, which only accepts connections from the same machine, or on a port the platform doesn't send traffic to.

Path 1 explains why, in lesson 1.4.3. The short version: online, requests arrive from outside the machine, so the app has to listen on 0.0.0.0 (every address the machine has) and on the port the platform expects.

Find it in the starter

Open server.js and look at the last lines:

app.listen(3000, "127.0.0.1", () => {
  console.log("Tasks app listening on http://127.0.0.1:3000");
});

Two things are hard-coded: the loopback address and port 3000. On your laptop both are fine, which is exactly why this kind of bug survives until the first deploy.

Agents write it this way for a good reason: tutorials and dev servers default to localhost, because on a laptop that's the safe choice. The agent ran the app, the loop's observe step saw it answer, and nothing it ran looked like the internet.

If npm start ever fails with EADDRINUSE, something else already uses that port. Pick another one with PORT, which the fix below makes possible.

Fix it

Replace those lines with:

const port = process.env.PORT || 3000;
app.listen(port, "0.0.0.0", () => {
  console.log(`Tasks app listening on port ${port}`);
});

The app now listens on every address, and on the port in the PORT environment variable, falling back to 3000 when it isn't set.

Prove both halves on your machine:

PORT=4000 npm start

In another terminal, curl http://localhost:4000/healthz should answer {"status":"ok"}. Stop it, run npm test to check nothing else broke, then commit:

git commit -am "fix: listen on 0.0.0.0 and the PORT variable"
git push

Ask the agent the right question

In your own app, the same bug hides in a framework's start command or config file, not always in a listen call. Ask your agent a question, not for a fix: "Where does this app choose its listen address and port? Show me the lines." Read the answer, then decide the change yourself. Lesson 1.4.3 has the setting for common frameworks.

Isn't 0.0.0.0 risky on my laptop?

On a shared network, 0.0.0.0 lets other devices on the same Wi-Fi reach your dev server. For a toy tasks app that's harmless. For anything with real data, you can read the address from a variable too, such as HOST, default it to 127.0.0.1, and set HOST=0.0.0.0 only where the app is deployed.

Check yourself

The starter works at localhost:3000. Deployed as-is, why would it fail?
You set PORT=4000 and start the fixed app. Which URL answers?

In the docs