---
title: Quickstart
description: Deploy a static site to Tori with npx tori.host and get a live URL.
---

This is the first static deploy. You need **Node.js 20 or newer** (for `npx`) and an email address. Nothing else: no account is created until the CLI opens a browser.

## 1. A directory with an `index.html`

```sh
mkdir hello-tori && cd hello-tori
cat > index.html <<'EOF'
<!doctype html>
<meta charset="utf-8">
<title>Hello from Tori</title>
<h1>Hello from Tori</h1>
EOF
```

If you already have a site that builds to `dist/`, `build/`, `out/`, or `_site/` with an `index.html` inside, use that project instead.

## 2. Deploy

```sh
npx tori.host
```

If this machine has never signed in, a browser opens. Sign in (or sign up — same click). The CLI packs the files, uploads them, and waits until the site answers:

```
✓ live: https://plucky-wren.tori.cloud
```

Open that URL. You should see the page you just wrote.

The first deploy creates the app and writes `tori.json` in the current directory:

```json
{
  "slug": "plucky-wren"
}
```

Keep that file in version control so later deploys update the same app. Commands such as `tori logs` and `tori env` read the slug from it.

## 3. Deploy again

Edit `index.html` and run `npx tori.host` from the same directory. The CLI reuses the slug in `tori.json`. `live` means the files of this deploy are the ones the URL serves. There is no separate promote step. Ctrl+C stops watching; it does not cancel an upload that already finished.

## What the CLI looks for

With no directory argument it uses `dist/`, `build/`, `out/`, or `_site/` when one of them contains `index.html`. Otherwise it runs `npm run build` and looks again, then `./index.html`.

Pass a directory to skip detection:

```sh
npx tori.host deploy ./dist
```

If none of those exist, it exits with `nothing to deploy` and names what it looked for. Build the site first, or pass the directory that holds `index.html`.

## Frameworks

Vite, Astro, and similar tools work when you deploy the **output** directory, not the source tree:

```sh
npm run build
npx tori.host deploy ./dist
```

`node_modules` and `.git` at the top of the packed directory are skipped. Do not pack `tori.json` into the site; the CLI already excludes it.

## If it is a server, not a site

A service is a different app kind: a container the zone runs for you. With a `Dockerfile` at the root, `npx tori.host` builds it on Tori; without one, say so and the language is detected:

```sh
npx tori.host deploy --service
```

If you already have an image, nothing is packed — the reference travels, digest-pinned:

```sh
npx tori.host deploy --image registry.example.com/acme/api@sha256:<64 hex>
```

The container must listen on port 8080 and declare a numeric non-root `USER`. See [Deploy a container service](/guides/service-deploy/) and [tori deploy](/cli/deploy/).

## If something fails

| What you see | What to do |
| --- | --- |
| Browser does not open (SSH, CI, no display) | Use the printed `https://my.tori.host/cli-auth` code, or set `TORI_TOKEN` in CI. [Sign in](/getting-started/#sign-in) |
| `no API token found, and this looks like CI` | Mint a token in the console under Settings → API tokens and export `TORI_TOKEN` |
| `nothing to deploy` | Create `index.html`, run the framework build, or pass `./dist` |
| `npm run build failed` | Fix the build, then deploy the output directory explicitly |
| Archive refused before upload | Drop large files; the CLI checks a local size cap, and the server also enforces the plan's artefact limit |
| `deploy failed: …` | The server stored a reason (`deploys.error`). Fix that, then deploy again. [Deploy status and recovery](/guides/deploys/) |

Download this page as [Markdown](/downloads/markdown/getting-started/quickstart.md).
