Prepare your app
A standard app needs nothing extra. This guide assumes the current TanStack Start (1.x on Vite 8, with Nitro), as in TanStack's own examples. Check two files:
vite.config.tsincludes Nitro:import { defineConfig } from 'vite' import { tanstackStart } from '@tanstack/react-start/plugin/vite' import viteReact from '@vitejs/plugin-react' import { nitro } from 'nitro/vite' export default defineConfig({ plugins: [tanstackStart(), viteReact(), nitro()], })package.jsonhas the scripts from TanStack's Node guide:"scripts": { "build": "vite build", "start": "node .output/server/index.mjs" }
We detect @tanstack/react-start and @tanstack/solid-start, so Solid apps work the same way. We build with NITRO_PRESET=node-server and run npm start. If there is no start script, we run node .output/server/index.mjs instead. The server reads the PORT we set, so no code change is needed.
Want something else? Set your own Start command in Settings → Build, or add a NITRO_PRESET env var, and we leave that part alone. Never use vite dev or vite preview as the start command.
Current TanStack Start needs Node 22.12 or newer. We build on Node 22.19 by default. If you set engines.node in package.json, keep it at 22.12 or above.
Server functions and API routes
Your app runs as one Node server, not as static files. Server functions (createServerFn) and server routes (a server.handlers block on a route) run on that server, so they can read env vars and talk to a database. SPA mode works too: the page renders in the browser, and server functions still reach the same server.
Environment variables
Add variables in the project's settings. They're available both while the app builds and while it runs, so build-time values (like framework-public keys) work without extra setup. See Environment variables.
- Only
VITE_variables reach the browser, throughimport.meta.env. They are baked in when the app builds, so add them before deploying and redeploy after a change. Never put secrets in them. - Server functions can read any variable with
process.env. Read it inside the handler, not at the top of a module, so a secret can't end up in the client bundle.
Add a database
Add a Postgres addon and DATABASE_URL is injected automatically. Read it with process.env.DATABASE_URL in server functions or server routes only. The browser can't see it, and shouldn't. The database isn't reachable while the app builds, so run migrations in front of the Start command in Settings → Build.
Health check
Every deploy requests your health check path (/ by default) and only goes live once it returns a 2xx or 3xx status. The platform keeps checking it after that. If / returns 404 or needs a login, add a route like /healthz that returns 200 and set it as the Health Check Path in Settings. Otherwise the deploy fails and the previous version keeps serving.
Deploy TanStack Start for free
The Free plan costs nothing and needs no card. It includes:
- One project, live over HTTPS at
<project-slug>.ethiodeploy.app. - 256 MB of memory and half a CPU core.
- A managed Postgres, MySQL or Redis addon with 512 MB of storage. It doesn't expire.
- A redeploy on every push, the same as the paid plans.
The catch is sleep. After 30 minutes without traffic the app stops, and the next request starts it again. We hold that request for up to 30 seconds while the app boots, so the first visitor after a quiet spell waits a little.
A TanStack Start app runs on the Free plan with no changes. The server, server functions and API routes all fit in one project, and the Postgres addon covers the database. Keep an eye on memory if you render heavy pages: the Free container has 256 MB.
When the app has to stay awake or needs your own domain, Pro is $10 or 1,399 ETB a month: always on, 1 GB of memory and a 5 GB database.
Deploy
- Sign in with GitHub and install the EthioDeploy app on the repo.
- Click “New Project” and pick the repo.
- Watch the build in the dashboard. When the health check passes, your app is live at
<project-slug>.ethiodeploy.app. Every push to the default branch redeploys, and the old version keeps serving until the new one is healthy.
Troubleshooting
Deploy succeeds but the site returns 503, or only static files load
Cause: The app was served as a static Vite site instead of running its server. That happens when TanStack Start isn't listed in package.json, or a custom start command points somewhere else.
Fix: Keep @tanstack/react-start or @tanstack/solid-start in package.json, or set the Start command to node .output/server/index.mjs.
Cannot find module .output/server/index.mjs
Cause: The build produced no Nitro server, usually because nitro() is missing from vite.config.
Fix: Install nitro and add nitro() from nitro/vite to your Vite plugins.
Build fails with "Cannot find native binding" or EBADENGINE warnings
Cause: engines.node pins a Node older than 22.12, which Vite 8 and TanStack Start need.
Fix: Raise engines.node (for example to "22" or "24"), or remove it to use our default, and redeploy.
An env var is undefined in the browser
Cause: It has no VITE_ prefix, or it wasn't set when the app was built.
Fix: Rename it with VITE_ (only if it's safe to expose), or read it in a server function. Then redeploy.
The app runs but the deploy fails its health check
Cause: Your health check path returns 404, a 5xx, or needs a login.
Fix: Add a route like /healthz that returns 200 and set it as the Health Check Path in Settings.
Still stuck? Check the deployment's build log in the dashboard, or see Troubleshooting.
Questions
No. We detect @tanstack/react-start or @tanstack/solid-start, build with NITRO_PRESET=node-server and run node .output/server/index.mjs (or your npm start). Your vite.config only needs the nitro() plugin from TanStack's Node setup.
Yes. The app runs as a long-running Node server, so server functions and server routes run on it and can read env vars and a database.
Yes. The Free plan runs one project with a 512 MB Postgres database at no cost. The app sleeps after 30 minutes without traffic and wakes on the next request, and the container has 256 MB of memory. Pro ($10/month) keeps it always on with 1 GB.
Yes. TanStack Start for Solid (@tanstack/solid-start) is detected and built the same way as the React version.
Ready to deploy?
Sign in with GitHub and pick your repo. No Dockerfile, no YAML.
More guides
All guides on the deploy guides page. Custom domains, env vars, addons and billing are in the docs.