Static sites
Any directory with index.html is deployable. The default and fastest path on VibeHost.
Static is the default runtime on VibeHost. If you have an index.html somewhere in a directory tree, you can deploy it — no build step required, no framework lock-in. Most apps you'll ever ship to VibeHost are static.
The simplest possible deploy
mkdir hello && cd hello
echo '<h1>Hello, vibe coding</h1>' > index.html
vibehost app create hello --json
vibehost link --app hello
vibehost deployThat's the entire flow. Output:
{
"ok": true,
"data": {
"id": "depl_abc...",
"url": "https://hello.vibehost.space",
"immutableUrl": "https://depl_abc.vibehost.space",
"deployKind": "static",
"status": "healthy"
}
}How static works
- Your dir is tarballed, validated (no
..traversal, no symlinks, size caps), and uploaded as content-addressed chunks. - Repeat deploys skip blobs the server already has — only changed files transfer.
- Assets land in our asset storage and are served directly from the edge. Routable in ~5 seconds from deploy. Zero cold start because there's no app-specific runtime for pure static.
- The edge serves index.html for
/, files by path for the rest, and a 404 fallback for misses (override with--spa, see below).
Framework recipes
Anything that emits a static dir works. The output directory name varies by framework — point vibehost deploy at whatever your build emits.
npm create vite@latest my-app
cd my-app && npm install
npm run build
vibehost app create my-app
vibehost deploy ./dist --app my-appnpm run build emits to dist/. Works with the React, Vue, Svelte, Solid, Preact, and vanilla templates — Vite always emits to dist/ by default.
For React Router / Vue Router with browser-history mode, add --spa:
vibehost deploy ./dist --app my-app --spanpm create astro@latest my-site
cd my-site && npm install
npm run build
vibehost app create my-site
vibehost deploy ./dist --app my-sitenpm run build emits to dist/. Astro emits to dist/ by default. Server-side rendered routes (output: 'server') won't work on the static runtime — keep output: 'static' (the default).
import adapter from '@sveltejs/adapter-static';
export default {
kit: {
adapter: adapter({
fallback: 'index.html', // for SPA-style routing
}),
},
};npm run build emits to build/:
npm run build
vibehost app create my-app
vibehost deploy ./build --app my-app --spaIf you have +page.server.ts (SSR-only) load functions, they won't run on the static adapter. Migrate to client +page.ts.
nuxi generate emits to .output/public/:
npx nuxi generate
vibehost app create my-nuxt-site
vibehost deploy .output/public --app my-nuxt-siteNuxt 3's nuxi generate (vs nuxi build) produces a pure static tree. Dynamic API routes (server/api/*) won't work on the static runtime.
import { vitePlugin as remix } from '@remix-run/dev';
export default {
plugins: [
remix({ ssr: false }), // SPA mode
],
};npm run build emits to build/client/:
npm run build
vibehost deploy ./build/client --app my-remix-app --spaRemix SPA mode emits a fully static bundle. Server loaders → client loaders; no loader function on routes that touch DB / fs.
No build step. Just a folder:
mkdir my-site
cat > my-site/index.html <<'EOF'
<!doctype html>
<title>Hi</title>
<h1>Hello, web</h1>
EOF
vibehost deploy ./my-site --app my-siteHugo emits to public/; Jekyll emits to _site/.
hugo
vibehost deploy ./public --app my-blog
bundle exec jekyll build
vibehost deploy ./_site --app my-blogMkDocs emits to site/; Docusaurus emits to build/; VitePress emits to .vitepress/dist/. Each group below is one generator.
mkdocs build
vibehost deploy ./site --app my-docs
npm run build
vibehost deploy ./build --app my-docs
npm run docs:build
vibehost deploy .vitepress/dist --app my-docsWhat makes the cut
The tarball must contain at least one index.html somewhere. The CLI auto-detects the document root.
Limits per archive (raise via Enterprise plan):
| Constraint | Default cap |
|---|---|
| Total archive size | 200 MB compressed |
| Per-file size | 50 MB |
| Entry count | 50,000 |
| Symlinks | Rejected (security) |
Absolute paths / .. | Rejected (security) |
| Empty archive | Rejected (no index.html) |
If your build exceeds 50,000 entries, that's almost always a node_modules leak — .next/cache, source maps from third-party deps, or a misconfigured build dir. Check what you're shipping:
vibehost deploy ./dist --dry-run --json | jq '.data.fileCount'SPA routing (history mode)
Single-page apps need a fallback so deep links don't 404:
vibehost deploy ./dist --spaThis adds a /* → /index.html rewrite. Without it, /dashboard returns 404 because no file at that path exists.
--spa is sticky on the app — re-applied on subsequent deploys until you turn it off. Toggle via dashboard, or check the current setting with:
vibehost app inspect my-app --json | jq '.data.spaFallback'Custom 404 / 500 pages
Add files at the conventional paths:
404.htmlat the root → served on misses (only if--spais off)500.html→ served on edge-level errors (rare; usually only if asset storage is unreachable)
These work without --spa. If you turn --spa on, index.html wins over 404.html for unknown routes.
Asset cache headers
The edge sets Cache-Control heuristically:
| File pattern | Default Cache-Control |
|---|---|
*.html | public, max-age=0, must-revalidate |
*.js, *.css, hashed file names (foo.abc123.js) | public, max-age=31536000, immutable |
*.png, *.jpg, *.svg, *.woff2, ... | public, max-age=86400 (24h) |
Override via _headers at the root:
# _headers
/*.html
Cache-Control: public, max-age=300
/assets/*
Cache-Control: public, max-age=31536000, immutable
X-Robots-Tag: noindexSyntax follows the Netlify _headers format (Netlify-style globs, [200] status filters, custom headers).
Redirects
Two ways: _redirects file (build-time) or platform redirects (no rebuild needed):
# _redirects (in your deploy dir)
/old-page /new-page 301
/blog/* /posts/:splat 301Or via CLI — these take effect without a rebuild:
vibehost redirects add /old-page /new-page --app my-site
vibehost redirects list --app my-site
vibehost redirects remove rdr_xyz --app my-sitePlatform redirects override _redirects if there's a conflict.
What you can't do (yet)
- No server-side logic — no edge functions, no middleware, no API routes on the static runtime.
- No build-time secrets —
vibehost env setexists but its values only reach the runtime, not your local build (build runs on your machine). Read from your shell env / dotenv during build. - No private static apps via build — visibility is set per-app (public / workspace / private). Private static apps are gated at the edge, not by the build.
- No streaming — static files are served with full
Content-Length; you can't stream a response from a static deploy.
Custom domains
vibehost domain add www.example.com --app helloThen add the printed CNAME record at your DNS provider. See Custom domains for full DNS setup, including Cloudflare-proxied domains.
Next
- Channels — preview deploys
- Custom domains — bring your own hostname
- Grants and visibility — control who sees the site