Deploy
tibi serve anywhere Docker runs, fronted by your platform of choice.Pick your path
- Static (all routes are
.tibifiles with no per-request dynamism, or dynamic routes whose params resolve at build time) → Cloudflare Pages viatibi deploy cf-pages. - SSR (live channels via
__channels, cron jobs, OAuth gating, dynamic params resolved per request) → container deploy usingDockerfile.tibi-serve. - Self-host (your own VM with a reverse proxy) → just run
tibi serve.
Not sure which? Start static. If your build summary shows - skipped /something (dynamic — no result.__params) for routes you actually need live, that's the signal to flip to the container path.
Static → Cloudflare Pages
Built into the CLI:
That runs tibi build + wrangler pages deploy dist/ --project-name my-app. Wrangler owns CF authentication via its own wrangler login flow — Tibi never touches your tokens. Install wrangler first if you don't have it:
You can drop the --project flag on every invocation by setting it in .tibi-config:
Useful flags:
--branch <name>— passes--branchthrough to wrangler so you can deploy preview environments.--skip-build— skip thetibi buildstep (handy when CI builtdist/in a previous stage).--dist <path>— override the upload directory (defaultdist).
What tibi build emits
Every .tibi page renders to a static HTML file; the SSG also emits a few Cloudflare-Pages-native files:
dist/<path>/index.html— one per static route, plus one per__paramsentry for dynamic routes (see below).dist/404.html— rendered frompages/_404.tibiif present. Pages serves this for any path that doesn't match a route.dist/_redirects— one line per[[redirects]]block in.tibi-config, in the format Pages expects (<from> <to> <status>). Author order is preserved; top-to-bottom first-match.
The build summary calls out every emission so you can verify what shipped:
Pre-rendering dynamic routes
A page whose URL pattern contains a dynamic segment (/blog/[slug].tibi → /blog/{slug}) can opt into build-time rendering by exporting result.__params from its frontmatter — an array of objects whose keys match the dynamic segments:
The SSG iterates each __params entry, substitutes the keys into the URL pattern, and writes one HTML file per entry (dist/blog/<slug>/index.html). Catchall patterns (/files/{*rest}) accept path-shaped values (rest: "a/b/c" → dist/files/a/b/c/index.html).
Pages with no __params (or an empty array) keep the pre-1.0.38 behaviour and get skipped — the build summary tells you which routes didn't ship, with a reason.
The data source is whatever the frontmatter language can reach: a JSON file in data/, a directory walk, even a build-time API call. Anything declarative can be expressed as a constant __params; we deliberately don't ship a separate [[routes]] params config block because the frontmatter form is strictly more powerful and keeps the data next to the page that consumes it.
Other static hosts (Netlify / Vercel / S3 / nginx)
Run tibi build in CI (or locally) and upload dist/. The bundled package.json has a preinstall hook that curl-pipes the Tibi installer, so every PaaS that runs npm install before the build gets the binary for free. _redirects and 404.html follow conventions Cloudflare Pages and Netlify both honour; Vercel/S3 ignore them but the rest of the site still serves correctly.
SSR → container deploy
Apps that need the SSR runtime (live __channels, [[cron]] jobs, OAuth gating via strands-auth, per-request dynamic params) need a long-running process. Tibi ships a reusable Dockerfile at the repo root:
The same Dockerfile works on:
- Cloudflare Containers (beta) — point the project at the built image; Cloudflare's edge fronts it. The natural pairing with Cloudflare Pages when one of your routes needs SSR but the rest is static.
- Fly.io —
fly launch, point at the Dockerfile, done. - Railway / Render — same pattern; both auto-detect
Dockerfile.tibi-serveif you set it as the dockerfile path. - Coolify / Dokku / your own Docker host — works unchanged.
Self-host
Any Linux box with the tibi binary on PATH + tibi serve behind a reverse proxy (nginx / Caddy / Traefik). The bundled installer drops the binary onto $HOME/.local/bin:
Same binary the container deploy ships. The Coolify docker-compose used by tibi.fi itself is a working example of the SSR-behind-proxy pattern; the docker-compose.yml at the repo root is the canonical reference.
Plugins in dist/
If your project uses Rust plugins, tibi build pre-compiles them into dist/plugins-bin/ for runtime hosts that don't have rustc. Those binaries ride along to Cloudflare Pages too but never execute there — Pages is a static host. Harmless if a little wasteful in image size; no action needed.