July 18, 2026 · Handbook

Manual: deploy targets and your own domain

This article describes the product at publication. See AI Builder and Agent Teams for current capabilities.

Deploying to your own server means handing an agent SSH-adjacent access to a box you're paying for, that other things might already live on. That's a different trust level than publishing to a free subdomain, and the setup reflects it — a few fields, filled in once, and then every build after that is a button. Here's what people actually ask before and after they wire one up.

What do I need to create a target?

Five things, in Settings → Deploy:

  1. A name you'll recognize later — "prod-vps", "client-hostgator", whatever survives a dropdown at 11pm
  2. Host and port
  3. SFTP credentials
  4. A webroot path

No API tokens, no CLI to install on the server, no cron job to babysit. If your host hands out SFTP access — which covers nearly every shared host, every VPS, every managed WordPress box — you're done in about two minutes.

Password or key?

Key, if your host supports it. Passwords work fine and we store them scoped to your account, but a key is one fewer secret sitting anywhere — the difference between "revoke a key" and "reset a password everywhere that password happened to get reused" if something goes wrong later. Plenty of cheap shared-hosting SFTP setups only offer password auth, and that's fine too. Just don't reuse that password anywhere else.

How do I find the right webroot path?

This is the field people get wrong the first time, because the wrong answer still looks plausible. It's not your home directory, it's not /var/www — it's the exact folder your web server is configured to serve from.

ServerTypical webroot
Apache / cPanelpublic_html
Nginx/var/www/mysite/html — or some path a previous developer named three years ago for reasons nobody remembers

If you're not sure, drop a throwaway test.txt into the folder you think is right using any SFTP client, then check whether it loads at yoursite.com/test.txt. Get this wrong and the deploy will still report success — the agent faithfully writes files into the wrong folder, and you're left staring at a live site that hasn't changed, wondering why.

Can one target cover more than one domain?

Yes, and this is the part that saves real time once you're past your first site. A target is one server and one set of credentials — it isn't tied to a single domain. In Domain Management you attach each domain to a target with its own webroot override. Run three sites off one VPS with Nginx server blocks?

  • site-a/var/www/site-a
  • site-b/var/www/site-b
  • site-c/var/www/site-c

One target, three attachments. You're not re-entering an SSH password three times, and you're not maintaining three near-identical targets that drift out of sync the day you rotate a key and forget one of them. Click deploy on any of the three domains and it already knows which server and which folder — you never pick at deploy time.

What does the agent actually do when it connects?

First, it looks around — read-only, nothing written yet. That inspection is checking for:

  • An empty folder
  • A previous version of this exact build
  • An old WordPress install
  • A "coming soon" placeholder your host dropped there by default

That decides the strategy. An empty webroot gets a straightforward upload. A webroot with something already in it gets handled more carefully, because plenty of real setups have things living alongside the site that shouldn't vanish:

  • A .well-known folder for SSL validation
  • An uploads directory nobody put in git
  • A wp-config.php nobody wants touched

The job here is closer to "figure out what changed and reconcile it" than "wipe and replace."

Then, before a single byte gets overwritten, the existing webroot is captured as a version on your own host. Not a database record, not a diff we compute and hope is right — an actual snapshot of what was sitting there. This matters most on the very first deploy to any target, because that deploy is always landing on top of something, even if the something is nothing. Empty folder, empty snapshot. Five-year-old static site nobody remembers building — preserved exactly, for free, before it's touched. That first deploy is also the one you're least sure about, so it's the one where this matters most.

Does it upload my source code or the built site?

Built site, always. For a static site that's the generated pages. For a framework build — Next.js, Vite, whatever the site type calls for — it's the compiled output, the dist or build folder, never the source tree. I think this is the right call even though it means you can't SSH in and run npm run dev against what's on the server. Uploading source would mean your production webroot needs a Node runtime and a build toolchain just to serve HTML — turning a shared-hosting box that was never meant to run a build pipeline into one, and turning every deploy into "hope the server has enough memory to finish npm install." Shipping only compiled output keeps the webroot exactly what a static file server expects. Boring. Boring is what you want at 2am when something's wrong and you're staring at that folder trying to work out what's actually being served.

How do I know a deploy actually worked?

After the upload, the agent hits the live URL and checks whether it resolves — not a 500, not a blank page. Whatever it finds, plus anything it noticed during inspection that it wants your input on ("this webroot has a wp-content folder I left untouched, confirm that's expected"), lands in the build's chat thread. That's the pattern across this whole platform: no silent success, no silent failure into a support ticket. The agent tells you what it saw and what it decided, in the same thread where you asked for the build.

What's actually in version history?

Every deploy adds a version — not just the first one. So the history isn't your builds plotted against an abstract timeline; it's the literal sequence of what was served from that webroot, in order, starting from whatever was there before you showed up. Version one is always that pre-platform state, captured automatically. You don't have to think about it.

What does revert actually restore?

The previous live version, exactly — not a re-run of an old build, not an approximation. The actual files that were serving traffic before. That's a meaningfully stronger guarantee than most "rollback" features I've used elsewhere, which usually mean "re-deploy from an old commit" and quietly assume your build process is deterministic and your environment hasn't drifted since. Here revert is a restore of a known-good snapshot, which is why it's safe to reach for under pressure — you're not reasoning about whether the rollback might behave differently than the thing it's rolling back to.

And the moment you actually need it is never a calm one; it's "the new build broke checkout and traffic is live right now."

One click, previous version restored, done. The reasoning behind treating this as a first-class feature rather than an afterthought is in Iterating without fear — worth reading once, before you need it. Both history and the restore control live on the build card and in the target's own history view.

Does this back up my database too?

No, and I'd rather say that plainly than let anyone assume otherwise. Version history on the host covers what this deploy pipeline put in the webroot. If your site has a database, or user uploads, or anything else mutating outside of deploys, that's a separate concern entirely — revert doesn't touch it and shouldn't be mistaken for a backup strategy that does.

Boundaries, stated plainly: the agent configures YOUR server with YOUR credentials — web server config where needed, the webroot, versions. It never touches DNS you didn't point, and credentials are stored scoped to your account and never shown to agents directly (see tenant isolation). SFTP only — no plain FTP, ever.
Handbook
ShareXLinkedInFacebookRedditQuoraWhatsAppTelegramEmail
← All posts