Deploying static sites
Deploy source with managed static builders or upload built files, and configure redirects, headers, and 404 pages.
A static site works with nothing but HTML, JS, CSS, and image files, no server. The files are served from the edge (servers close to your visitors), so it is fast and needs almost no setup. You can deploy framework source with a managed preset or upload an already-built folder.
Deploy
Install the CLI
curl -fsSL https://nitroship.co/install.sh | bash
ntro loginAlready installed? Skip ahead. Full options, PATH setup, and updates: Install the CLI.
Build from source with a managed preset
Deploy the project root, not its output folder. Nitroship installs dependencies, runs the framework build, and publishes the resulting static files.
ntro apps create my-static-site
ntro deploy . --prod| Project | Managed static build |
|---|---|
| Vite or Create React App | Vite and React SPAs |
| Angular | Angular, configured for static output |
| Gatsby, Docusaurus, VitePress, Eleventy | Static generators, built with the Node toolchain |
| Hugo, Jekyll, MkDocs | Static generators, built in a generated toolchain container |
| Frameworks with static modes | Follow the static-mode setup for Nuxt, Astro, SvelteKit, or Qwik |
| Plain HTML or already-built files | Static HTML |
Both static (a Node-toolchain build or plain files) and container-static (a toolchain image that exports only the built site) publish files to the CDN without a compute runtime. A framework that emits a server instead uses a managed Node bundle or container; it is not a static deployment merely because it has client assets.
Detection first honors build.framework in nitroship.json, even when a root Dockerfile exists. It cannot be combined with build.dockerfile. Without an explicit framework, Nitroship checks a configured Dockerfile or root Dockerfile.nitroship/Dockerfile, then the dedicated Next.js adapter, then the managed framework registry, and finally generic static files.
For a generic static build, configure the build command and output directory:
{
"build": {
"framework": "static",
"command": "npm run build",
"outputDirectory": "dist"
}
}Use the framework slug instead of static when you want its preset to choose the build and output settings. Follow the linked framework guide for its build requirements.
--local supports static and managed Node builds. For container-static generators such as Hugo, Jekyll, and MkDocs, drop --local so the remote builder can run the toolchain image.
Upload already-built files
If you build yourself, deploy the folder that holds your build output.
ntro apps create my-static-site
ntro deploy ./dist --app my-static-site --prodA subfolder like ./dist has no app link, so add --app. Deployed files never change; deploy again to update them.
Address rules
Put nitroship.json in the folder you deploy (the deployment root) to set address rules. A vercel.json from Vercel is read in the same format. If both exist, only nitroship.json is read and vercel.json is ignored.
| Setting | What it does |
|---|---|
cleanUrls | Serves /about.html at /about. Addresses ending in .html redirect to the version without it. |
trailingSlash | Always add (true) or always remove (false) the / at the end of addresses. |
redirects | Sends an old address to a new one. 307 (temporary) by default, 308 (permanent) with "permanent": true. |
rewrites | Shows a different file while keeping the address. Applies only when no real file matches. |
headers | Adds response headers, such as caching rules, for matching paths. |
routes | Low-level rules written as regular expressions, for when the above is not enough. |
Example: clean addresses, a redirect, and a custom 404
{
"cleanUrls": true,
"redirects": [
{"source": "/old/:slug", "destination": "/docs/:slug", "permanent": true}
],
"headers": [
{
"source": "/assets/(.*)",
"headers": [{"key": "Cache-Control", "value": "public, max-age=31536000, immutable"}]
}
],
"routes": [
{"handle": "filesystem"},
{"src": "^/.*$", "dest": "/404", "status": 404}
]
}- The file
/about.htmlopens at/about. /old/hellopermanently redirects to/docs/hello;:slugcarries over whatever was in that position.- Files under
/assets/are cached by browsers for a year. Use this only for files whose names include a content hash, so a new version gets a new name. {"handle": "filesystem"}means "if a real file matches at this point, serve it". Rules after it apply only when no file matched, so unknown addresses show404.htmlwith status404.
Unknown setting names are treated as typos and the deployment is rejected. After editing the file, deploy once to check that it is accepted.
The repository's examples/hello-static includes index.html, 404.html, clean addresses, response headers, and caching for hashed files.
Files that are not uploaded
So secrets are not uploaded by accident, these are excluded by default: files starting with .env (.env.example is still uploaded), .git, and .npmrc.
Anything else you want to leave out goes in a different file depending on how you deploy.
| Default deploy (remote builder) | --local direct upload | |
|---|---|---|
| Ignore file | .gitignore, .ntroignore | .nitroshipignore |
| Always excluded | node_modules, .next, .ntro | The .nitroship folder |
Re-include with ! rules | Yes (e.g. !.env.production) | No |
Symbolic links are not served as static site files.