Instance cloning
Every production Instance starts as a clone. The Gateway copies a candidate Instance’s committed source, stored environment, and optionally one SQLite database to a new Instance on anapp-prod Node. The candidate keeps running. The clone ends with a prepared production home and no release. Its first deployment selects code. Provisioning creates the production PHP runtime through the native provisioner and projects its Route through the shared native Route projector.
instance:create on an app-prod Node returns instance.candidate_required. The app-prod role decides this, not APP_ENV.
Prepare the Node
The destination Node needs its own TLD for the preview domain. Set it when you add the Node:Clone
Name the candidate, the destination Node, the new Instance name, and a preview name:POST /api/v1/instances/{candidate}/clone.
The request accepts no Project, commit, Unix user, or path. The candidate decides the Project. Laravel-package Projects use their declared Project type for source classification; they do not need an
artisan application to clone. The caller needs an access grant to both Nodes.
The result names the new Instance, its branch, its preview domain, and its selected release, which is empty.
Candidate rules
The candidate is an active development Instance with its checkout, or an active production Instance with a selected release. A development candidate must have its recorded branch checked out, not another branch or a detachedHEAD. Its source must have no staged, unstaged, untracked, or submodule change. Its current commit must be in the Project repository. The Gateway checks this on the candidate’s Node using its recorded source identity.
Every Git invocation in the candidate inspection, including calls through sudo and checks inside submodules, passes -c core.hooksPath=/dev/null and -c core.fsmonitor=false. Development candidate checks that inspect file contents run as the Node’s managed user without a credential environment. instance.clone_candidate_unavailable carries the inspection’s stderr in details.stderr, at most 1,000 characters, with URL credentials and token-shaped words replaced by [REDACTED].
A checkout’s hooks and custom filesystem monitor do not run during that check. These overrides leave the candidate’s stored Git configuration unchanged; they do not relax the clean-source rules.
Destination checks
The destination must be an active Linux Node with an activeapp-prod role. The Project can have one production Instance per Node. In an active Cluster, the Cluster needs an active Router.
Orbit inspects the destination checkout as its production user from a directory that user can access. A private SSH account home does not prevent source classification. For a nested Laravel app, classification uses composer.json and artisan in the application directory derived from the effective web root; Git identity checks still use the complete repository. Foreign file ownership, unsafe source metadata, and failed directory scans still stop the clone.
What the clone gets
The new Instance gets its own copy of each part below.
Before the clone completes, Orbit renders the new Instance’s stored values and writes its
.env in the production home. The first deployment links that file from the release’s application directory, including a nested path such as apps/site/.env. It copies no .env file from the candidate, and no cached configuration, dependencies, logs, caches, or PHP-FPM tuning. Stored values such as APP_KEY copy as they are. References such as {{instance.domain}} resolve against the new Instance.
Preview domain
The preview domain is<preview-name>.<node-tld>. For example, shop.com on a Node with TLD prod.orbit gives shop.com.prod.orbit. Orbit never uses the Cluster TLD here.
Orbit stores the preview as an explicit private Route, so a later TLD change does not rename it. On a standalone Node the Route has Node scope. In an active Cluster it has Cluster scope, and private DNS points to the Router. Cloning publishes no public listener. Replace the preview with the production domain through a separate Route change.
A missing Node TLD returns route.tld_required. A domain that another Route owns returns route.domain_conflict.
SQLite seed
sqlite_source_path must be inside the candidate’s checkout or selected release, and it must be a readable SQLite database. Orbit takes a consistent snapshot while the candidate keeps writing, checks its integrity, and installs it as <home>/database.sqlite, owned by the new Instance’s user. The Gateway checks the path and disk space on both Nodes before it replaces anything. Without the option, the clone gets no database.
Cloning seeds a different production Instance at the fixed <home>/database.sqlite path. Instance transfer instead keeps the Instance ID between development Nodes and installs the snapshot at the selected file’s relative path in the destination checkout. Each transfer attempt has its own snapshot record. Transfer rollback abandons that seed and removes its owned temporary files before closure. An identical retry after restoration takes a fresh snapshot rather than reusing data captured before the source restarted. These transfer rules do not change the clone’s destination or its identical-request retry.
The snapshot holds whatever the candidate committed at that moment, queued jobs included. Remove copied queue rows or other data on the new Instance before you start its workers. Other databases and external storage need their own preparation.
Candidate stays live
Cloning never stops the candidate’s Processes or Schedules, pauses queues, or changes its source, environment, or database.Retry
The Gateway records the clone request and each finished step. An identical request resumes an interrupted clone, even when the candidate has moved to a newer commit. A request that changes the candidate, Node, name, preview, branch, or SQLite path returnsinstance.clone_retry_conflict. After completion, an identical request returns the same Instance and changes nothing. instance:create does not create or resume a production Instance.
While a clone is incomplete, removal of its candidate returns instance.clone_in_progress.
Errors
The Gateway returns these codes for a clone. A failure after reservation records its code on the new Instance, and the identical request resumes it.Next steps
- Update environment values that must differ from the candidate, then synchronize to rewrite
.env. - Clean copied application data, such as queue rows.
- Record deploy steps.
- Run
instance:deployto create and select the first release. - Start the copied Processes and Schedules.