The short answer
Localhost is your own computer. It has your files, your .env settings, your database and your installed tools. A server starts clean and has only what you send it. When an app works on localhost and fails online, something your computer had is missing on the server.
The fastest way to find it is to read the failure reason and the logs. The nine causes below cover most cases.
The nine usual causes
-
The code still points at localhost
A line such as
fetch("http://localhost:3001/api")works on your computer and nowhere else, because on a serverlocalhostmeans the server itself. Check: search your code forlocalhostand127.0.0.1. Fix: use a relative path such as/api, or read the address from an environment variable. -
The app listens on the wrong port or the wrong address
The platform tells your app which port to use in the
PORTenvironment variable, and it must listen on0.0.0.0, not127.0.0.1. Check: the deploy says "built successfully but never started listening". Fix: read the port fromPORTand bind to0.0.0.0. See the full fix. -
An environment variable is missing
Your
.envfile is on your computer. The server only has the variables you set for it. Check: the logs mentionundefined, or a key is "missing". Fix: set the variable, then redeploy. See environment variables. -
The database only exists on your laptop
A
DATABASE_URLthat points tolocalhostcannot work online. Check: look for it in your.env. Fix: use a hosted Postgres database. Antideploy creates one for your app and setsDATABASE_URLfor you. See add a database. -
Files saved to disk disappear
Anything your app writes to its own disk is gone on the next deploy. That includes uploads and a SQLite database file. Check: search for
multer,fs.writeFileorsqlite. Fix: use a database for data and a bucket for files. See file uploads. -
A package is installed on your machine but not in package.json
Your computer may have a tool installed globally, or a lock file that is out of date. The server installs only what the manifest and lock file say. Check: the build says a command is "not found", or that
package-lock.jsondoes not match. Fix: add the package and commit the lock file. See the lock file error. -
It only works in development mode
next devandvite devare forgiving. A real build is strict: type errors fail it, and code that runs at import time can break it. Check: run the production build on your machine, for examplenpm run build. Fix: fix what the build reports. -
A file name has a different capital letter
Windows and macOS usually ignore capital letters in file names. Linux servers do not.
Button.tsxandbutton.tsxare different files there. Check: the build says "Module not found". Fix: make the import match the file name exactly. -
A timer inside the app never fires
A scheduler such as
node-cronneeds the app to be running, and an idle app sleeps. Check: a nightly job never ran. Fix: move the work into a route and use a scheduled job that calls it. See scheduled jobs.
How to find out which one
- Read the failure reason. Every failed deploy records one sentence that names the cause.
- Read the logs. They show what the app printed before it stopped.
- Match the message. The Fix an error section of this blog has a page for each common message.
Your coding agent can do all three. Say "the deploy failed, read the reason and the logs and fix it". See how your agent reads logs and fixes a failed deploy.