Publishing and Updating Live Sites with the Netlify CLI: A Guide for Coding Agents

How a coding agent can use the Netlify CLI to publish a built site or artifact to a live URL non-interactively, capture that URL, and push later revisions to the same address.

Agent Guide

An agent that writes code can also ship it. The Netlify CLI turns any built directory β€” a dist/ folder, a single index.html, a static export β€” into a live URL with one command, and every later revision goes to the same URL with the same command. This guide is written for that workflow: non-interactive, scriptable, and safe to run in a loop.

This guide is CLI-only. For programmatic integration from a backend service, see the Netlify API instead. Commands and flags below were checked against Netlify CLI v26.1.0; run netlify --version and consult the command reference if yours differs.

What β€œan artifact” means here

Anything you have already built into static files:

  • a framework build output (dist/, build/, out/)
  • a single index.html and its assets
  • a static export from any tool

The CLI uploads finished files. It can run your build first, or skip the build entirely and upload a folder as-is β€” which is the common case for an agent that has already produced the output.

Authenticate without a browser

Interactive login (netlify login) opens a browser, which an agent cannot do. Instead, generate a personal access token once (Netlify UI β†’ User settings β†’ Applications β†’ New access token) and pass it as an environment variable:

export NETLIFY_AUTH_TOKEN=your_token_value

Every CLI command reads this variable and authenticates silently (Netlify CLI: get started). Store it as a secret in whatever runs the agent; never hard-code it, and scope it as narrowly as your setup allows rather than reusing a full-access personal token across many agents. If you’d rather pass the token per command than set it in the environment, every command also accepts --auth $NETLIFY_AUTH_TOKEN.

Publish a throwaway artifact with no account

For a one-off β€” an artifact you just want a shareable URL for, with no site to manage and no token β€” deploy anonymously:

netlify deploy --dir=dist --no-build --allow-anonymous --json

This uploads the folder and returns a claimable URL: the site exists without an account. The deploy output includes a site ID and a drop token; a human claims the site within one hour by running netlify claim --site <site-id> --token <drop-token> (or by logging in). Unclaimed sites are removed after one hour, so this is for disposable previews only β€” use the authenticated flow below for anything you’ll revise or keep (deploy command reference).

Create a site to publish to

A deploy needs a target site. Creating one interactively prompts for a team and name β€” an agent should create it explicitly instead:

netlify sites:create --name my-project --account-slug your-team-slug

--name sets the subdomain (my-project.netlify.app); --account-slug picks the team to create it under (sites:create reference). Find your team slug with netlify teams:list or in the Netlify UI under Team settings β€” note that the CLI calls a team an β€œaccount” in some flags (--account-slug here, --team on deploy), so the two names refer to the same thing. If the name is taken, the command reports it β€” pick another rather than retrying the same one.

Alternatively, create the site and deploy in a single command: netlify deploy accepts --site-name <name> (which implies site creation if it doesn’t exist) together with --team <slug>, so netlify deploy --site-name my-project --team your-team-slug --dir=dist --no-build --prod does both at once.

To reuse an existing site instead of making a new one, link the current folder to it:

netlify link

This writes a siteId into .netlify/state.json in the project folder, so subsequent deploys from that folder target the linked site with no further flags (Netlify CLI: get started).

In a stateless environment β€” a fresh container per run, where .netlify/state.json won’t survive β€” don’t rely on linking. Capture the site ID once from sites:create --json (the id/site_id field), persist it wherever the agent keeps state, and pass --site <site-id> explicitly on every deploy. That makes each deploy self-contained and independent of the working directory.

First publish

Two things matter to an agent here: which URL you get, and getting the URL back in a form you can parse.

Deploy a prebuilt folder to a draft URL (a unique, unlisted preview address):

netlify deploy --dir=dist --no-build --json
  • --dir=dist uploads that folder as the site.
  • --no-build skips the build β€” use it when you have already built the output, which is the usual agent case (deploy command reference).
  • --json prints the deploy result as JSON so you can read the URL programmatically instead of scraping human-readable stdout.

The JSON includes deploy_url (the draft address) and, on a production deploy, url (the live production address). Parse that field rather than regexing the console text.

Promote the same output to the production URL with --prod:

netlify deploy --dir=dist --no-build --prod --json

This publishes to the site’s stable production URL (my-project.netlify.app or a custom domain). The draft-first flow is useful when you want to verify a build at its preview URL before promoting it; if you just want it live, deploy straight to --prod.

If you do want Netlify to run the build for you (it has a repo or a build command configured), drop --no-build and the CLI runs the build before uploading.

Capture the live URL

Because --json emits structured output, an agent reads the URL directly:

URL=$(netlify deploy --dir=dist --no-build --prod --json | jq -r '.url // .deploy_url')
echo "Live at: $URL"

The .url // .deploy_url fallback keeps this robust across deploy types: deploy_url (the draft address) is always present, while url (the production address) appears on a production deploy. Hand $URL back to the user. Do not parse the decorated stdout banner β€” it is formatted for humans and its layout is not a stable contract.

The revision loop: updating a published site

This is the part an agent repeats. To ship a revision, rebuild your output and run the same deploy command against the same site:

# after editing and rebuilding into dist/
netlify deploy --dir=dist --no-build --prod --json

Three properties make this safe to run repeatedly:

  • The production URL is stable. Every production deploy replaces what’s live at the same address. The user’s link never changes across revisions.
  • Deploys are atomic and immutable. Each deploy is a complete, versioned snapshot. A new deploy goes live all at once; it does not patch files in place. Nothing serves a half-updated site mid-deploy.
  • Rollback is instant. Because every deploy is retained as an immutable snapshot, you can revert to any previous one from the Netlify UI (Deploys β†’ select a deploy β†’ Publish deploy) without rebuilding. An agent that ships a bad revision can tell the user exactly how to roll back.

So the mental model is: build β†’ deploy β†’ same URL, new immutable snapshot. Repeat as many times as the task needs.

One cost note for loops: on credit-based plans each production deploy costs 15 credits, but draft and preview deploys are free (how credits work). So an agent iterating rapidly should deploy to draft URLs (omit --prod, see the next section) and promote to production only when a revision is ready β€” that keeps iteration free and avoids burning credits on every intermediate build.

Preview a revision before it goes live

When you want the user to review a change without touching the production URL, deploy to a named draft instead of production:

netlify deploy --dir=dist --no-build --alias=review-42 --json

--alias fixes the subdomain of the draft URL, so you get a predictable preview address (useful when you want the same review link across a few iterations) rather than a random one (deploy command reference). Alias strings are capped at 37 characters. Omit --prod and the production URL stays untouched until you promote a build to it.

A complete agent recipe

End to end, from nothing to a live URL and one revision:

# 0. Auth (once, from a stored secret)
export NETLIFY_AUTH_TOKEN=your_token_value
# 1. Create the site (once)
netlify sites:create --name my-project --account-slug your-team-slug
# 2. Link this folder to it so later deploys need no --site flag
netlify link --name my-project
# 3. Build your output however the project builds (agent's job)
npm run build # produces dist/
# 4. Publish to production and capture the URL
URL=$(netlify deploy --dir=dist --no-build --prod --json | jq -r '.url')
echo "Live at: $URL"
# 5. Later: edit, rebuild, redeploy β€” same command, same URL
npm run build
netlify deploy --dir=dist --no-build --prod --json | jq -r '.url'

For a single HTML file with no build step, point --dir at the folder that contains it and skip step 3 entirely.

Common gotchas for automated runs

Deploying the wrong folder. --dir must point at the finished output (dist, build, out), not the project root. Uploading the root serves your source files, not your site.

Forgetting --no-build on a prebuilt folder. Without it, the CLI may try to run a build command. If the output is already built, --no-build keeps the deploy fast and predictable.

Relying on the interactive prompt. A first netlify deploy with no linked site prompts the human to pick or create one β€” which stalls an agent. Create the site with sites:create and link it (or pass --site) so nothing ever blocks on input.

Parsing stdout instead of --json. The human-readable output is not a stable interface. Always add --json and read the field you need.

Missing NETLIFY_AUTH_TOKEN. Without the token in the environment, commands fall back to interactive login and hang. Confirm the variable is set before the first command.

Resources