You described what you wanted, an assistant wrote it, and after some back and forth it works. You can see it at localhost:3000. It does the thing.
Then you try to show someone, and there is nowhere to send them.
If you have spent the last few days getting increasingly frustrated at this stage, here is the useful thing to know first: you have not hit the limits of your ability. You have hit a different job. Writing the app is about behaviour β what happens when someone clicks the button. Deploying it is about everything the code assumes and never says out loud: that a database is running, that the machine has the right runtime, that something is listening on the right port, that certificates exist, that it starts again after a reboot.
An assistant is very good at the first job because the feedback loop is tight and local. The second involves a machine you do not have yet, network configuration, and a dozen decisions nobody told you were decisions. It is not harder, exactly. It is unfamiliar, and it fails in ways that give you very little to search for.
Almost everyone building this way hits the same wall. It is the single most common place a working project quietly stops.
Before anything else, the honest fork in the road.
If your app is a website with no backend β static pages, or a React/Vue/Svelte front end that talks to APIs in the browser β you do not need a server. Push it to GitHub, connect the repo to Cloudflare Pages, Netlify or Vercel, and it is online in about ten minutes on a free tier. Stop reading and go and do that.
If your app has a backend and a small database β a Next.js app with a few API routes, a small Flask or Express service β start with Vercel, Railway, Render or Fly. They are genuinely good, the free and cheap tiers are real, and you will be running today rather than learning Linux.
A server becomes worth it when one of these is true: the platform bill has grown past what a Β£5 server would cost, you need background jobs or scheduled tasks that keep running, you need a proper database that is definitely still there tomorrow, you are storing files that must persist, or you have data that has to live in a particular country.
Nobody should move to their own server as step one. It is a fine destination and a terrible starting point.
Whichever route you take, AI-written code tends to share a specific set of assumptions. None of these are the assistant being wrong β they are perfectly sensible for code running on your laptop, and they all stop being sensible the moment it is somewhere else.
API keys, database passwords and tokens written directly into a source file. Fine on your machine, catastrophic in a public repository β bots scan GitHub for exactly this, within minutes of a push.
Move them to environment variables, add .env to .gitignore, and if a key has ever been committed, rotate it. Deleting it in a later commit does not remove it from history.
If your app uses SQLite, or writes to a file next to the code, that data lives on the machine's disk. On most hosting platforms the filesystem is ephemeral: it is rebuilt on every deploy, and everything written since last time is gone. People discover this by launching, collecting a week of signups, pushing a small fix, and losing all of them. Use a managed Postgres or MySQL, or a database on a server with a real disk.
Same problem, different data. Anything users upload β avatars, documents, images β needs object storage such as Cloudflare R2 or Amazon S3, not a folder in the project.
localhost is hardcoded somewhere
Almost always in at least one place: an API base URL, a database connection string, a redirect after login, a CORS setting. On your machine localhost means "this computer", which is correct. In production it means the server talking to itself, and the request never reaches the user.
Search your project for localhost and 127.0.0.1 before you deploy anything.
npm run dev, flask run, python manage.py runserver β these are built for one developer on one machine. They are slow under load, they often leak memory, and several will happily display a full stack trace, including secrets, to whoever triggers an error.
Every framework has a production mode and a production server. Use it.
Your app runs because you typed a command in a terminal that is still open. Close the laptop, reboot the server, or let the process crash once, and it is simply off β with nothing to tell you, until someone mentions the site is down.
Production needs something that starts the app on boot and restarts it when it dies. That is what Docker's restart policies, or a systemd service, are for.
The most expensive one, and the last to be noticed. Taking a backup is easy; the question is whether it restores.
A backup can be well-formed, restore without a single error, contain every table β and hold no data at all. A --schema-only flag copied from a StackOverflow answer, or a backup user that can read the schema but not the rows, produces a file of plausible size that restores perfectly into an empty database.
If you take one thing from this list: at some point, restore your backup somewhere disposable and check the rows are actually in it. Most people never do this once. You will need each of these. This is the whole shape of it, so it stops being a fog:
It is roughly a day and a half the first time, most of it spent on things that are obvious in hindsight and impossible to search for beforehand. The second time it is an afternoon.
Pick the honest one:
And whichever you pick: the fact you could not immediately deploy it does not mean you should not have built it. You made something that works. Getting it online is a different, smaller, entirely learnable problem β and one where the answer is written down, which the building rarely is.
Originally published at shipops.dev.