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.
Building and deploying are genuinely separate skills
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.
First: you might not need a server at all
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.
The seven things that break in production
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.
1. Secrets are in the code
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.
2. The database disappears every time you deploy
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.
3. Uploaded files vanish for the same reason
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.
4. 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.
5. You are running the development server
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.
6. Nothing restarts 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.
7. There are no backups, and "there are backups" is not the same thing
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.
If you do go with your own server
You will need each of these. This is the whole shape of it, so it stops being a fog:
- A server. A small virtual machine from Hetzner, DigitalOcean, AWS or similar. Around £5–17 a month depending on provider.
- A hardened login. Key-based SSH, root login disabled, a firewall, automatic security updates. This is the part people skip, and servers get compromised within days of being reachable with a password.
- A container setup. Docker and Docker Compose, so the app runs the same way on the server as it does for you.
- A reverse proxy with HTTPS. Something that answers on port 443, gets a certificate automatically and renews it. Caddy does this with almost no configuration.
- A database that persists, on a volume that survives restarts and updates.
- Backups that leave the server, to storage in your own account, verified by restoring them.
- A way to deploy changes without SSHing in and hoping — ideally push to your main branch and let it happen.
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.
What to do next
Pick the honest one:
- Static site, no backend? Cloudflare Pages, ten minutes, free. Go now.
- Backend, early, few users? Railway or Render. Deal with servers when there is a reason to.
- Outgrown that, or need control? Work through the list above. I've open-sourced the stack I use for it — provisioning, HTTPS, backups and deploys — at github.com/patrickmackin05/shipops-stack, MIT licensed and free to take.
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.
This article was originally published by DEV Community and written by patrickm0.
Read original article on DEV Community