VibeHost
Guides

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 deploy

That'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-app

npm 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 --spa
npm create astro@latest my-site
cd my-site && npm install
npm run build
vibehost app create my-site
vibehost deploy ./dist --app my-site

npm 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).

svelte.config.js
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 --spa

If 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-site

Nuxt 3's nuxi generate (vs nuxi build) produces a pure static tree. Dynamic API routes (server/api/*) won't work on the static runtime.

vite.config.ts
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 --spa

Remix 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-site

Hugo emits to public/; Jekyll emits to _site/.

hugo
vibehost deploy ./public --app my-blog

bundle exec jekyll build
vibehost deploy ./_site --app my-blog

MkDocs 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-docs

What 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):

ConstraintDefault cap
Total archive size200 MB compressed
Per-file size50 MB
Entry count50,000
SymlinksRejected (security)
Absolute paths / ..Rejected (security)
Empty archiveRejected (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 --spa

This 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.html at the root → served on misses (only if --spa is 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 patternDefault Cache-Control
*.htmlpublic, 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: noindex

Syntax 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 301

Or 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-site

Platform 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 secretsvibehost env set exists 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 hello

Then add the printed CNAME record at your DNS provider. See Custom domains for full DNS setup, including Cloudflare-proxied domains.

Next

On this page