Skip to content
flypod
CLI reference

flypod [path]

Deploy a directory or single file to a public flypod URL; owned and permanent when logged in, otherwise anonymous with a 14-day TTL.

The default command deploys a directory or single file. Logged in, the deploy is owned and permanent. Anonymous, it lives 14 days; running flypod login later automatically claims every anonymous site this CLI remembers, so you don't need to track tokens to make a deploy permanent.

Synopsis

flypod [path] [flags]
flypod deploy [path] [flags]

flypod deploy is the same command spelled out. The path can be a folder or a single file; for several files, put them in a folder and deploy the folder.

The first flypod in a folder creates a site. Every later flypod in that folder updates the same site, exactly as flypod update does: same URL, a new version, and the flypod update receipt. To make a separate site from a folder that already has one, pass --new. If the folder's site is gone (deleted, or expired), flypod makes a new one, as if the folder had never deployed.

With no path, flypod auto-detects the first of these directories that contains an index.html:

dist/
build/
out/
.output/public
.vercel/output/static
public/
_site/

A candidate with no index.html of its own counts when exactly one folder inside it has one (Angular's dist/<app>/browser). Failing all of them, a folder of plain pages (an index.html and no package.json, _config.yml or other project file) deploys as it is. A project root is never deployed by guesswork: when nothing is found, the no_build_dir error's fix names the build command and the folder it writes, such as Run `npm run build`, then `flypod ./dist`.

The screen goes to stderr, and by default it is short: a confirmation, the URL, the command that ships a change, and when the site ends. Warnings always show. --verbose prints the full receipt. When stdout is not a terminal (a pipe, URL=$(flypod)), the URL alone is also printed there; in a terminal it is not repeated. In a non-TTY context, animation is stripped automatically. On success, flypod saves a folder-to-site link so flypod update knows where to ship next.

The deploy output shows no token. The site's manage_token is saved to projects.json (mode 0600) keyed by the folder you deployed from, which is how update, rollback, and versions work with no arguments. flypod token reveals it if you ever need the plaintext.

Flags

FlagDescription
--jsonEmit a single JSON object (see below).
--verboseMore of the same receipt. In a terminal: the rollback and history commands, and the site id, version id and source folder. With --json: also the site's tokens, the source folder, file and byte counts, timing, the expiry as days and as an ISO date, and a next_note.
-q, --quietPrint the URL on stdout. Nothing else.
--timelineTurn on History: a chip on the site's pages that opens earlier versions, and a page that compares two. Off by default, because it makes replaced versions public. See the version timeline. --history is the same flag.
--no-timelineTurn History off. --no-history is the same flag. If both are given, off wins.
--newMake a separate new site, even though this folder already has one. The earlier site stays live, and this folder then updates the new one.
--commentsTurn comments on for the site, in anonymous mode. Needs a signed-in deploy: without one, the deploy still succeeds and the receipt says comments_skipped.
--comments --mode <mode>Same, choosing the mode: anonymous, passcode, or approved. Any other value fails with usage before anything is uploaded.
-h, --helpPrint usage and exit.
-v, --versionPrint the CLI version and exit.

Examples

Deploy the auto-detected build directory:

flypod
https://a3f1c2d4e5b60789.flypod.page

Deploy an explicit directory:

flypod ./dist

Machine-readable output:

flypod ./dist --json
{
  "ok": true,
  "action": "created",
  "url": "https://a3f1c2d4e5b60789.flypod.page",
  "site_id": "a3f1c2d4e5b60789",
  "version_id": "8f1a0c2e44b7d913",
  "expires_at": 1782929906088,
  "healed": ["spa-fallback"],
  "warnings": [],
  "link_saved": true,
  "next": {
    "update": "flypod update",
    "versions": "flypod versions",
    "rollback": "flypod rollback",
    "keep": "flypod login --json"
  }
}

action is "created" on a folder's first deploy. A flypod in a folder that already has a site answers with the flypod update receipt, whose action is "updated".

On a deploy made while signed in, expires_at is null and next has no keep entry. While the site expires, next.keep is flypod login --json: see signing in from an agent.

Some keys appear only when they apply:

KeyWhen
skippedSomething in the folder was left out (secrets, version control, dependencies, .flypodignore matches, links pointing outside the folder). Each entry has a path and a reason.
replaced_link--new ran in a folder that was already linked to another site. { site_id, url } of the previous site, which is still live and still expiring.
comments_enabled, comments_mode--comments was passed. When it could not be applied because the deploy was not signed in, also comments_skipped: "not_signed_in" and comments_fix.
timeline, history_url--timeline or --no-timeline was passed. timeline is the state the site is actually in (a refused switch does not fail the deploy); history_url is present while it is on.
manage_tokenOnly when link_saved is false (see below).

The full receipt:

flypod ./dist --json --verbose
{
  "ok": true,
  "action": "created",
  "url": "https://a3f1c2d4e5b60789.flypod.page",
  "site_id": "a3f1c2d4e5b60789",
  "version_id": "8f1a0c2e44b7d913",
  "expires_at": 1782929906088,
  "manage_token": "…",
  "claim_token": "…",
  "source": "dist",
  "files": 12,
  "bytes": 48213,
  "elapsed_ms": 742,
  "healed": [],
  "warnings": [],
  "link_saved": true,
  "expires_in_days": 14,
  "expires_at_iso": "2026-10-19T12:00:00.000Z",
  "next": { "update": "flypod update", "versions": "flypod versions", "rollback": "flypod rollback", "keep": "flypod login --json" },
  "next_note": "This folder is linked to a3f1c2d4e5b60789. Run `flypod update` (or plain `flypod` again: in a linked folder it updates) to publish changes to this same URL…"
}

Notes

  • warnings lists problems that survived the deploy. Each has a code and a fix_recipe naming the change to make. A deploy can succeed and still be broken for visitors, so check warnings.length before you treat the URL as good. healed lists what flypod fixed for you — spa-fallback means it recognised a single-page app and unknown paths now serve index.html. Both arrays are always present, so no guard is needed.
  • The default receipt carries no tokens. They are credentials that cannot be rotated, the CLI has saved what it needs, and a receipt is what gets pasted into chats and logs. The one exception: when link_saved is false the receipt includes manage_token, because nothing else is holding it.
  • next holds the literal commands for what comes after. Use next.update to ship a change; in a linked folder plain flypod does the same. When the link could not be saved, every next command carries --site <id>.
  • flypod --new in a folder that is already linked makes a separate site and points the folder at it. It says so before uploading (one line on stderr under -q), and the --json receipt carries replaced_link plus a new-site-in-linked-folder warning whose fix_recipe says the earlier site stays live, flypod again ships to the new one, and flypod update --site <old id> ships to the earlier one.
  • When flypod runs as an update and the update is refused, the error's fix ends by naming flypod --new as the way to make a separate site from this folder instead.
  • Keys that don't apply are omitted, not set to null: under --verbose an owned deploy has no claim_token key, an anonymous one has no owner_account_id key.
  • expires_at is epoch milliseconds on anonymous deploys, and null when the site is owned.
  • Use --quiet in scripts to capture the URL cleanly. It prints the URL on stdout and nothing else — no token.
  • What is uploaded: everything in the folder except .git, .hg, .svn, node_modules, .DS_Store, Thumbs.db, .env and .env.*, at any depth. Add gitignore-style patterns to a .flypodignore in the folder to leave out more. What was left out is listed after the deploy (skipped in --json).
  • Against flypod.dev, the CLI checks the size limits (200 files, 10 MB) before uploading and fails with too_large, naming the limit, instead of uploading everything to be refused. A self-hosted server sets its own limits and the CLI leaves the check to it.
  • A path that does not exist fails with no_build_dir (or unknown_command when it is a bare word close to a verb), before anything is uploaded.
  • To make an anonymous deploy permanent later, just run flypod login — it claims every anonymous site this CLI remembers, in one step.
  • See Tokens and ownership for how account sessions, manage_token, and claim_token differ.

Authenticate first with flypod login to get permanent, owned deploys from the start.

If flypod cannot write the folder link — a read-only or ephemeral FLYPOD_CONFIG_DIR, the classic CI container — it says so loudly and prints the manage_token, because at that point you are the only possible holder of it. Under -q that same case emits the token on stderr. This is the one moment the token belongs on screen.

On this page