Check this first
A green deploy means your code built and the container started. It does not mean the app has everything it needs to serve a request. The overwhelming majority of “deployed but broken” cases are one of four things, and your site’s Environment tab will usually tell you which.
Open your site → Environment. If we can tell what the app needs, it is listed there, along with why we think so.
1. It needs a database and doesn’t have one
If your Environment tab says “This app needs a database to work”, the app installs a database driver (pg, mysql2, mongoose, prisma…) but nothing has told it where the database is. Every request that touches data will fail, usually with a 500.
- Go to Databases → Deploy a database and pick the engine named in the warning.
- Copy its connection string once it is running.
- Back on your site’s Environment tab, add it under the variable the warning names — usually
DATABASE_URL.
Saving a variable redeploys the site automatically. You do not need to press Redeploy afterwards.
2. It needs API keys or secrets
The Environment tab lists the variables your app looks likely to need. Where the heading says “Required by this app”, your repository declared them itself (in .env.example, app.json or render.yaml). Where it says “Likely needed”, we inferred them from the packages your app installs — check them against your own documentation, because inference can be wrong.
Secrets we can safely generate for you — session and JWT signing keys — are marked, and filled in with a secure random value when you click them. Anything belonging to a third party (Stripe, Twilio, an AI provider) has to come from that provider’s own dashboard.
3. Only half your app deployed
If your repository holds a frontend and a backend — a React app at the root with an API in server/, for example — those are two applications, and one deployment runs one application. This is how Vercel and Railway work too.
When you pick a repository we scan it, and if we find more than one deployable app we say so and let you choose which one this deployment runs, using the Root Directory field. To run both:
- Deploy the first one (say the frontend, Root Directory left empty).
- Deploy a second site from the same repository, setting Root Directory to the other app — e.g.
server. - Tell the frontend where the API lives by setting its API URL variable to the API site’s address, e.g.
VITE_API_URL=https://your-api.hostwares.app.
Symptom to watch for: the site loads, but every login or data request fails. That is a frontend talking to an API that was never deployed.
4. API routes return HTML instead of JSON
Single-page apps (React, Vue, Svelte, Angular built with Vite) need every in-app URL to serve index.html so the router can take over — otherwise /login would 404 on refresh. We configure that for you.
We deliberately exclude /api/* from that rule, so a call to a backend that is not deployed returns a clean 404 rather than an HTML page. If your frontend reports something like “Unexpected token < in JSON”, it is calling an API that is not there — see section 3.
“The deploy server is busy right now”
Our build system rate-limits itself in short bursts when a lot is happening at once. We retry automatically, and this message only appears if the retries were also refused. Nothing was changed when you see it — wait a few seconds and repeat the action.
Still stuck
- Logs tab — your container’s own output. Startup crashes and missing-variable errors appear here first.
- Deployments tab — the build log, if the failure happened while building rather than running.
- Ask HW — our AI can read your logs and configuration and will usually name the cause directly.
- Open a ticket — include your site name and we will look at the container ourselves.