Zero-config web deployment with bini-deploy — generates hosting config and pushes straight to GitHub.
bini-deploy scans your project, generates the right hosting configuration for your target provider, and pushes it straight to GitHub — no YAML spelunking, no platform-specific docs to read first.
It's bundled into every Bini.js scaffold and exposed as npm run deploy. This page covers the web hosting providers it supports. For desktop and mobile targets, see Deployment Overview.
Installation
Already included in every Bini.js scaffold. To add it to an existing project:
npm install --save-dev bini-deploy
Supported Providers
Provider
Runtime
Config generated
Node.js Node.js (default)
Node (bini-server)
None — bini-server handles it, bini-deploy just pushes to GitHub
Netlify Netlify
Edge Functions (Deno)
netlify.toml
Vercel Vercel
Node.js Runtime
vercel.json
Cloudflare Cloudflare Workers
Workers
wrangler.toml
Deno Deno Deploy
Deno
server/index.ts
Node is the default because Bini.js ships with bini-server, a zero-dependency production server. Choosing it skips config generation entirely — there's nothing to adapt, so bini-deploy just commits and pushes.
How it works
Scan — scans src/app/api/ for route files
Generate — creates the platform-specific entry file and config
Clean — removes leftover entry files, config, and directories from any previously selected platform, including when switching to Node or a native platform
Push — commits and pushes everything to your GitHub repository
Deploy — your hosting provider deploys automatically from GitHub
Usage
Every Bini.js scaffold already has this wired into package.json, so deploying is just:
npm run deploy
This runs bini-deploy interactively — it prompts you to pick a platform (web, windows, macos, linux, android, ios) and, if you choose web, a hosting provider (node, netlify, vercel, cloudflare, deno).
For scripts and CI, skip the prompts with flags:
npx bini-deploy --platform web --hosting vercel --repo https://github.com/you/your-app --yes
Flag
Description
--platform
web, windows, macos, ios, linux, android
--hosting
web only — node (default), netlify, vercel, cloudflare, deno
--repo
GitHub repository URL to push to
--generate-entry
generate only the production entry file — netlify, vercel, cloudflare, deno
--yes, -y
skip interactive prompts and use the flags provided
Node.jsNode.js
The default hosting choice. bini-server reads your API handlers directly from src/app/api/ at request time — no build step, no generated entry file.
npm run deploy
Pick web, then node (the default) when prompted.
Works out of the box on Railway, Render, Fly.io, or a bare VPS with pm2. See Production Server for the full runtime reference.
NetlifyNetlify
API routes run as Netlify Edge Functions.
npm run deploy
Pick web, then netlify when prompted.
Generates:
netlify.toml
netlify/edge-functions/api.ts
netlify.toml
[build]
command = "vite build"
publish = "dist"
[[edge_functions]]
path = "/api/*"
function = "api"
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
Edge Functions run on Deno, not Node — packages depending on Node built-ins (fs, nodemailer) won't work there.
VercelVercel
API routes run on Vercel's Node.js Runtime.
npm run deploy
Pick web, then vercel when prompted.
Generates:
vercel.json
api/index.ts
Vercel reads api/index.ts before running your build — bini-deploy commits it for you, so it's already there when CI runs. It imports hono as an npm package, so bini-deploy checks it's installed and tells you the exact install command if it's missing.
CloudflareCloudflare Workers
API routes run as a Cloudflare Worker.
npm run deploy
Pick web, then cloudflare when prompted.
Generates:
wrangler.toml
worker.ts
Or deploy manually with Wrangler once the entry file exists:
npx wrangler deploy
Like Vercel, the worker entry imports hono as an npm package — bini-deploy verifies it's installed before generating the entry file.
DenoDeno Deploy
API routes run on Deno.
npm run deploy
Pick web, then deno when prompted.
Generates:
server/index.ts
In the Deno Deploy dashboard, set:
Entrypoint:server/index.ts
Runtime: Dynamic App
Deno Deploy also reads its entry file before building — same reasoning as Vercel above. Deno and Netlify both import hono directly from a URL, so no local install check is needed for these two.
API Routes & Hono
Every provider mounts the same file-based routes from src/app/api/, dynamic segments and catch-alls included:
Each route exports a default handler that accepts a Request and returns a Response (or a JSON-serializable value):
src/app/api/users/[id].ts
export default async function handler(req: Request) {
const id = new URL(req.url).pathname.split('/').pop();
return { id, name: 'Ada Lovelace' };
}
Imports from hono are detected automatically and mounted as a full Hono app instead:
src/app/api/hello/route.ts
import { Hono } from 'hono';
const app = new Hono();
app.get('/', (c) => c.json({ message: 'Hello from Hono!' }));
app.post('/', async (c) => {
const body = await c.req.json();
return c.json({ received: body });
});
export default app;
ESM projects: since Bini.js projects ship with "type": "module", Node's native ESM loader requires relative imports to include their file extension. bini-deploy's generated imports already include .js, but if your route files import local helpers (e.g. ./utils), include the extension there too (./utils.js) or the deployed function will crash with ERR_MODULE_NOT_FOUND even though the build succeeds.
Automatic CORS
API routes get permissive CORS headers out of the box on every non-Node hosting adapter — Netlify, Vercel, Cloudflare, and Deno — so your frontend can call them without extra setup.
Git Push Behavior
Existing remote — used without modification
New projects — the provided URL is added as origin
No remote updates — once a remote is set, it's never changed
Always main — always pushes to main, automatically renaming master if needed
Remote-ahead recovery — if the remote has commits you don't have locally (e.g. GitHub auto-created a README), bini-deploy fetches and merges automatically with --allow-unrelated-histories -X ours, keeping your local version of any file that exists on both sides. A warning prints before the merge runs; a real conflict stops the process and prints manual recovery steps
This means you can run bini-deploy multiple times without accidentally pushing to the wrong repository.
Troubleshooting
Build fails with Cannot read properties of undefined (reading 'readFile')
Your typescript dependency resolved to TypeScript 7.x, which shipped as a Go-native rewrite without a public compiler API. Some hosting-provider build pipelines break on it. Pin typescript to a ^6.x release in package.json instead of using latest.
Zero-config web deployment with bini-deploy — generates hosting config and pushes straight to GitHub.
bini-deploy scans your project, generates the right hosting configuration for your target provider, and pushes it straight to GitHub — no YAML spelunking, no platform-specific docs to read first.
It's bundled into every Bini.js scaffold and exposed as npm run deploy. This page covers the web hosting providers it supports. For desktop and mobile targets, see Deployment Overview.
Installation
Already included in every Bini.js scaffold. To add it to an existing project:
npm install --save-dev bini-deploy
Supported Providers
Provider
Runtime
Config generated
Node.js Node.js (default)
Node (bini-server)
None — bini-server handles it, bini-deploy just pushes to GitHub
Netlify Netlify
Edge Functions (Deno)
netlify.toml
Vercel Vercel
Node.js Runtime
vercel.json
Cloudflare Cloudflare Workers
Workers
wrangler.toml
Deno Deno Deploy
Deno
server/index.ts
Node is the default because Bini.js ships with bini-server, a zero-dependency production server. Choosing it skips config generation entirely — there's nothing to adapt, so bini-deploy just commits and pushes.
How it works
Scan — scans src/app/api/ for route files
Generate — creates the platform-specific entry file and config
Clean — removes leftover entry files, config, and directories from any previously selected platform, including when switching to Node or a native platform
Push — commits and pushes everything to your GitHub repository
Deploy — your hosting provider deploys automatically from GitHub
Usage
Every Bini.js scaffold already has this wired into package.json, so deploying is just:
npm run deploy
This runs bini-deploy interactively — it prompts you to pick a platform (web, windows, macos, linux, android, ios) and, if you choose web, a hosting provider (node, netlify, vercel, cloudflare, deno).
For scripts and CI, skip the prompts with flags:
npx bini-deploy --platform web --hosting vercel --repo https://github.com/you/your-app --yes
Flag
Description
--platform
web, windows, macos, ios, linux, android
--hosting
web only — node (default), netlify, vercel, cloudflare, deno
--repo
GitHub repository URL to push to
--generate-entry
generate only the production entry file — netlify, vercel, cloudflare, deno
--yes, -y
skip interactive prompts and use the flags provided
Node.jsNode.js
The default hosting choice. bini-server reads your API handlers directly from src/app/api/ at request time — no build step, no generated entry file.
npm run deploy
Pick web, then node (the default) when prompted.
Works out of the box on Railway, Render, Fly.io, or a bare VPS with pm2. See Production Server for the full runtime reference.
NetlifyNetlify
API routes run as Netlify Edge Functions.
npm run deploy
Pick web, then netlify when prompted.
Generates:
netlify.toml
netlify/edge-functions/api.ts
netlify.toml
[build]
command = "vite build"
publish = "dist"
[[edge_functions]]
path = "/api/*"
function = "api"
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
Edge Functions run on Deno, not Node — packages depending on Node built-ins (fs, nodemailer) won't work there.
VercelVercel
API routes run on Vercel's Node.js Runtime.
npm run deploy
Pick web, then vercel when prompted.
Generates:
vercel.json
api/index.ts
Vercel reads api/index.ts before running your build — bini-deploy commits it for you, so it's already there when CI runs. It imports hono as an npm package, so bini-deploy checks it's installed and tells you the exact install command if it's missing.
CloudflareCloudflare Workers
API routes run as a Cloudflare Worker.
npm run deploy
Pick web, then cloudflare when prompted.
Generates:
wrangler.toml
worker.ts
Or deploy manually with Wrangler once the entry file exists:
npx wrangler deploy
Like Vercel, the worker entry imports hono as an npm package — bini-deploy verifies it's installed before generating the entry file.
DenoDeno Deploy
API routes run on Deno.
npm run deploy
Pick web, then deno when prompted.
Generates:
server/index.ts
In the Deno Deploy dashboard, set:
Entrypoint:server/index.ts
Runtime: Dynamic App
Deno Deploy also reads its entry file before building — same reasoning as Vercel above. Deno and Netlify both import hono directly from a URL, so no local install check is needed for these two.
API Routes & Hono
Every provider mounts the same file-based routes from src/app/api/, dynamic segments and catch-alls included:
Each route exports a default handler that accepts a Request and returns a Response (or a JSON-serializable value):
src/app/api/users/[id].ts
export default async function handler(req: Request) {
const id = new URL(req.url).pathname.split('/').pop();
return { id, name: 'Ada Lovelace' };
}
Imports from hono are detected automatically and mounted as a full Hono app instead:
src/app/api/hello/route.ts
import { Hono } from 'hono';
const app = new Hono();
app.get('/', (c) => c.json({ message: 'Hello from Hono!' }));
app.post('/', async (c) => {
const body = await c.req.json();
return c.json({ received: body });
});
export default app;
ESM projects: since Bini.js projects ship with "type": "module", Node's native ESM loader requires relative imports to include their file extension. bini-deploy's generated imports already include .js, but if your route files import local helpers (e.g. ./utils), include the extension there too (./utils.js) or the deployed function will crash with ERR_MODULE_NOT_FOUND even though the build succeeds.
Automatic CORS
API routes get permissive CORS headers out of the box on every non-Node hosting adapter — Netlify, Vercel, Cloudflare, and Deno — so your frontend can call them without extra setup.
Git Push Behavior
Existing remote — used without modification
New projects — the provided URL is added as origin
No remote updates — once a remote is set, it's never changed
Always main — always pushes to main, automatically renaming master if needed
Remote-ahead recovery — if the remote has commits you don't have locally (e.g. GitHub auto-created a README), bini-deploy fetches and merges automatically with --allow-unrelated-histories -X ours, keeping your local version of any file that exists on both sides. A warning prints before the merge runs; a real conflict stops the process and prints manual recovery steps
This means you can run bini-deploy multiple times without accidentally pushing to the wrong repository.
Troubleshooting
Build fails with Cannot read properties of undefined (reading 'readFile')
Your typescript dependency resolved to TypeScript 7.x, which shipped as a Go-native rewrite without a public compiler API. Some hosting-provider build pipelines break on it. Pin typescript to a ^6.x release in package.json instead of using latest.