Best Practices
deplo.ai works hardest when your repository follows a few boring conventions. Every recommendation here maps directly to how the platform detects, builds, and wires your app — nothing is style preference.
Repository layout that detects cleanly#
The detection engine reads your full repo tree and classifies it as a single app, a frontend + backend pair, or a monorepo. All three work — the key is keeping the boundaries obvious:
- One app per folder. A
client/(orfrontend/) folder and aserver/(orbackend/,api/) folder is the clearest layout. Env scoping also follows these folders — a variable read underserver/is backend-scoped. - Standard monorepo tooling is recognized — Turborepo, Nx, and pnpm/npm/yarn workspaces (e.g.
apps/web+apps/api). Declare workspaces properly rather than inventing a custom layout. - Don't mix two apps in one folder. A React app and an Express server sharing one
package.jsonforces the detector to pick one framework and confuses build resolution. - Keep framework config files (
next.config.ts,vite.config.ts, …) at their app's root — they are strong, heavily-weighted detection signals.
package.json scripts are the source of truth#
For Node backends, deplo.ai runs your repo's own build and start scripts — it never invents commands. That makes the contract simple:
- Both scripts must exist and work from a clean clone:
npm ci && npm run build && npm startis exactly what production does. startmust run the built output (e.g.node dist/index.js), not a dev watcher likenodemonorts-node-dev.- Build-time tools (TypeScript, bundlers) belong in
devDependencies; runtime deps independencies. - Commit your lockfile — the package manager (npm, yarn, pnpm, bun) is detected from it, so the same manager installs in production as on your machine.
Environment variable hygiene#
The scanner finds variables by reading your code, and uses example env files for safe defaults:
- Commit a
.env.examplewith every key — a value makes the variable optional with that default; leaving it empty keeps it required. It also documents your app for humans. - Never commit a real
.env. deplo.ai deliberately ignores values in real env files, but git history does not forgive — treat a committed secret as leaked and rotate it. - Reference variables directly (
process.env.STRIPE_KEY,import.meta.env.VITE_MAPS_KEY,os.getenv("DATABASE_URL")) so the scanner sees them. Dynamic access likeprocess.env[name]with a computed name is invisible to static scanning. - Never put secrets in client-prefixed keys (
NEXT_PUBLIC_*,VITE_*) — they are compiled into the public browser bundle.
Backend server conventions#
Two lines of server code prevent the majority of avoidable backend failures:
// 1. Listen on the platform's port, bound to all interfaces.
app.listen(Number(process.env.PORT) || 4000, "0.0.0.0");
// 2. CORS from the injected frontend origin — never a hardcoded domain.
app.use(cors({ origin: process.env.FRONTEND_URL }));- Listen on
process.env.PORT— Render assigns the port. A hardcoded port means the platform never sees your server come up. - Bind
0.0.0.0, notlocalhost— containerized traffic arrives on the external interface. - Read CORS origins from
FRONTEND_URL— deplo.ai injects your frontend's real production origin into the backend, and even corrects it automatically if Vercel assigns a different domain than predicted.
Secrets management#
- Rotate provider keys deliberately. Your Render API key is rotated by reconnecting with a new key in Dashboard → Integrations — the new key is validated live before it replaces the old one. Keys are stored encrypted (AES-256-GCM), masked in the UI, and never logged.
- Revoke CLI tokens you no longer use — from Dashboard → Settings. Give each machine and CI pipeline its own token so one revocation never breaks another environment. See CLI Authentication.
- Keep app secrets in env vars, provided through the deployment flow — they land as native env vars on your own Vercel project and Render service, where you can audit them any time.
Production readiness checklist#
| Check | Why it matters |
|---|---|
npm run build and npm start work from a clean clone | These exact scripts run in production — no local-only tooling. |
Backend listens on process.env.PORT, bound to 0.0.0.0 | Render assigns the port; wrong binding looks like a hung deploy. |
CORS reads process.env.FRONTEND_URL | The injected origin survives domain changes without a code edit. |
.env.example lists every key; no real .env committed | Drives detection, defaults, and required-ness — and keeps secrets out of git. |
| Lockfile committed | Production installs with the same package manager and versions as your machine. |
Frontend API calls use the injected key (e.g. NEXT_PUBLIC_API_URL) | The live backend URL is wired in at build time — no hardcoded localhost. |
| First deploy verified end-to-end, monitoring checked | Uptime probes and the latest-attempt banner (Monitoring) catch regressions early. |
Dashboard, CLI, or CI?#
Dashboard#
Best for first-time setup (provider connections, GitHub App install), filling in environment variables with the detected list in front of you, and reading logs, AI diagnoses, and monitoring.
CLI#
Best for day-to-day redeploys without leaving the terminal — deplo deploys the repo you're standing in, and deplo logs follows the pipeline. See the CLI overview.
CI / hands-off#
After your first successful deployment, a GitHub push webhook is registered automatically — pushes to the deployed branch redeploy with the previous configuration, no CI setup required. For explicit pipeline control (deploy only after tests pass, custom gates), run the CLI in CI with a DEPLO_TOKEN — see CI Usage.