Deploying an App You Built With an AI Coding Tool — A Checklist
An app built with Claude, Cursor, Lovable, or v0 usually runs perfectly on your machine and then fails to deploy for the same handful of reasons every time — it binds to localhost, keeps its database next to the code, has secrets pasted into source files, and has no Dockerfile. None of these are hard to fix, and none of them are the model's fault: it wrote code for the machine it could see. This is the checklist to run before you upload, in the order that things actually break.
1. Find out what actually starts it
The first question a build has to answer is "what command runs this?", and AI-generated projects are often ambiguous about it — three entry points, two of them scaffolding, one real.
Before deploying, confirm you can start the app from a clean checkout with one command. If you can't say what that command is, the build can't either. Delete the entry points you don't use; a main.py that's a leftover example and an app.py that's the real thing is exactly the ambiguity that produces a deployed app that starts the wrong file.
2. Bind to 0.0.0.0, not localhost
This is the single most common failure, and the most confusing one, because the deploy succeeds. Generated code very often contains host="127.0.0.1" or localhost — correct on your laptop, unreachable inside a container, where 127.0.0.1 means "this container and nothing else".
Change it to 0.0.0.0 and read the port from the environment rather than hard-coding it:
import os
uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", 8080)))
If your app deploys cleanly and the URL returns 502, this is almost always why — there's more on diagnosing that, including how to check from inside the container.
3. Get the secrets out of the source
AI tools put API keys in the file they're editing, because that's where the code is. Before you deploy — and before you push anywhere — search your project for the obvious ones: sk-, Bearer , token =, password =, connection strings with credentials in them.
Move each to an environment variable:
api_key = os.environ["OPENAI_API_KEY"] # not "sk-proj-..."
Rotate anything that was ever in a file, especially if the project touched a public repo or a shared chat. A key in git history is still a key.
4. Decide where data lives before you have any
Generated apps write next to themselves — ./app.db, ./uploads/, ./data.json. Locally that's your project folder. In a container it's the writable layer, which is discarded every time the container is recreated, which happens on every redeploy.
Pick a directory, mount a volume at it, and point the app there:
DB_PATH = os.environ.get("DB_PATH", "/data/app.db")
Do it before you have data you care about — a volume added later starts empty, and the old file is in the layer you're about to discard.
5. Make sure the dependency list is real
Two failure modes here, and both are common in generated projects:
- Missing dependencies. The model imported something it never added to
requirements.txtorpackage.json. It works locally because the package is in your environment from something else. - Phantom dependencies. A package that doesn't exist, or a version that doesn't. This fails the build loudly, which is the better outcome.
The check that catches both: install from your manifest into a clean environment and start the app. A fresh virtualenv or npm ci in a clean clone is a two-minute test that saves a cycle of failed builds.
6. Clean the folder before you zip it
Don't upload node_modules/, venv/, .venv/, __pycache__/, dist/, or .git/. They're reinstalled or rebuilt during the build, and they're usually most of the size — the upload cap is 50 MB, and it's almost always these directories that blow past it.
Keep your manifests (package.json, requirements.txt, pyproject.toml), your source, and any config or asset the app reads at runtime.
What you don't have to fix
The list above is short on purpose. Some things you can genuinely skip:
- You don't need a Dockerfile. Upload the project as a zip and the build agent reads your source, works out the runtime and dependencies, and generates the build for you.
- You don't need to restructure the project. Unusual layouts are fine as long as the entry point is unambiguous.
- You don't need to remove the framework's dev server config. It's ignored in production if your start command doesn't use it.

If the build fails, the agent reads the container logs, diagnoses it, and makes one corrective attempt — so a missing system package or a wrong start command often fixes itself. It can also pause and ask you for secrets it can tell are needed, which handles step 3 for anything you missed.
After it's up
Two things worth knowing on day one:
- A crash-looping app still reports as running. The platform restarts your process automatically, so check the logs for the exit-code line rather than trusting a green status. Details here.
- Watch memory. Generated code sometimes loads a whole dataset at startup or keeps everything in a list. On a small quota that's an out-of-memory kill, which looks like an unexplained restart rather than an error.
FAQ
Do I need a Dockerfile to deploy an app an AI wrote?
No. Upload the project as a zip and the build agent generates the build configuration from your source. Add a Dockerfile only if you want control over the image.
Why does my AI-generated app deploy but return 502?
It's almost certainly binding to 127.0.0.1 instead of 0.0.0.0, or listening on a different port than the one you exposed.
Where should the database go?
On a volume, not next to your code. Files written outside a volume are discarded whenever the container is recreated, including on every redeploy.
What should I remove before zipping?
node_modules, venv, .venv, __pycache__, dist, and .git. Keep source, manifests, and runtime assets. The upload limit is 50 MB.
How do I know if the dependency list is complete?
Install from your manifest into a clean environment and start the app. Anything missing shows up immediately rather than during a build.
What if my project has several entry points?
Delete the ones you don't use, or be explicit about the start command. Ambiguity here is the most common cause of an app that starts the wrong file.
Wrapping up
None of this is about AI-generated code being bad. It's that a model writes for the environment it can observe, and your laptop has a filesystem that persists, a loopback interface that works, and a shell where your API key is already exported. A container has none of those. Six changes — an unambiguous entry point, 0.0.0.0, secrets in the environment, data on a volume, an honest dependency list, and a clean zip — cover essentially every deploy failure that comes from that gap.