Apps and deployments
district app create publishes a repository as an app with one target. Each repository has at most one app. district app delete removes the app with every target and custom domain it holds. district deploy builds the repository and serves the build on one target of its app, or deploys an existing build with --from. Each command reads the clone in the current directory when no argument names a repository.
district app create
district app create [<repository>] [--name <name>] [--slug <slug>] [--workspace <id>] [--production | --staging | --dev]Publishes a repository as an app and prints the app's id and its primary target on stdout. Without a positional argument, the command reads the clone in the current directory and resolves the repository and the environment exactly as district build does. Without --slug, district-api derives the target label from the repository slug and adds a numeric suffix when that label is taken.
Arguments
| Argument | Description |
|---|---|
[<repository>] | The repository, by repo_ id or by slug. Without it, the clone in the current directory names one: its origin must be a District remote, which also names the environment. |
Flags
| Flag | Description | Default |
|---|---|---|
--name <name> | The app's display name. | the repository slug |
--slug <slug> | The label of the app's primary target. Without it, district-api derives the label from the repository slug. | the repository slug |
--workspace <id> | The workspace of the repository, when the same slug names a repository in more than one workspace. | - |
--production | --staging | --dev | The environment to act in. Pass one at most. Without a flag, the command uses the environment that the last district login recorded, or production. | - |
Output
$ district app create
created shopfront (happ_01K4…)
hostname shopfront.ondistrict.netFor a repository that already has an app, a taken, reserved, or recently released label, or a repository the caller cannot update, the API returns its own message. A reserved label that the command derived from the repository slug also names --slug, because no repeat of the command can change the derived label.
Exit codes
| Exit code | Meaning |
|---|---|
| 0 | The app exists. Its id and its primary target are printed. |
| 1 | The API refused the call with its own message: the repository already has an app, the label is taken, reserved, or recently released, or the caller cannot update the repository. |
| 2 | The command line names no repository, or a slug that several workspaces hold. |
district app delete
district app delete [<repository>] [--workspace <id>] [--yes] [--production | --staging | --dev]Hides the repository's app for 14 days, with every hostname and every custom domain it holds, and the repository with it when its kind is HOSTED_APP. An applet's source repository stays live. Each hostname answers 404, and the hostnames, custom domains and repository stay held. A restoreDeletion call with the printed deletion ID brings them back until the printed time. A custom domain does not stop the deletion. The purge after the printed time deletes the deployments, their scripts, the hostnames and the repository.
Without a positional argument, the command reads the clone in the current directory, as district app create does. The command prints the app, the environment, and one row per hostname with the custom domains that serve it, as district target list prints them, then asks Delete <label>? [y/N], where <label> is the label of the primary hostname, or the display name for an app that has no primary hostname.
Arguments
| Argument | Description |
|---|---|
[<repository>] | The repository, by repo_ id or by slug. Without it, the clone in the current directory names one: its origin must be a District remote, which also names the environment. |
Flags
| Flag | Description | Default |
|---|---|---|
--workspace <id> | The workspace of the repository, when the same slug names a repository in more than one workspace. | - |
--yes | Skips the "Delete <label>? [y/N]" question. A session with no terminal needs it. | false |
--production | --staging | --dev | The environment to act in. Pass one at most. Without a flag, the command uses the environment that the last district login recorded, or production. | - |
Output
$ district app delete --yes
deleted shopfront (happ_01K4…, deletion del_01K4…, restorable until 2026-09-08T09:00:00Z)A repository with no app is a usage error that names district app create. A caller who cannot delete the app is told the app was not found.
Exit codes
| Exit code | Meaning |
|---|---|
| 0 | The app, its hostnames, its custom domains and its repository are hidden until the printed time. |
| 1 | The confirmation was declined, or the API refused the call with its own message: the caller cannot delete the app. |
| 2 | The command line names no repository, a slug that several workspaces hold, a repository with no app, or no terminal can confirm. |
district deploy
district deploy [<repository>] [--target <slug> | --new-preview] [--ref <ref> | --from <id>] [--workspace <id>] [--yes] [--json] [--production | --staging | --dev]Builds the repository and serves the build on one target of its app. The command starts district build and uses the deployment that the build's deploy step creates. The repository, the ref, the confirmation, and the step lines behave as they do for district build.
The target is the app's primary target. --target <slug> names another target of the app. --new-preview creates a preview target with the build. Its label is generated, and it expires 7 days after its last successful deployment. --target with --new-preview is a usage error, because each build has one deploy intent.
The deployment binds the addons of the wrangler.jsonc section that the target's environment selects, as Bindings and environments describes.
--from <id> deploys a build that exists and builds nothing. The id names a deployment (hdep_…), whose build is deployed, or a build (hbld_…). The new deployment uses the environment of the target it serves, so --from moves a build from a preview target to a durable target, or from one environment to another. The target, the confirmation, the deployment status lines, the report and --json behave as for a deploy that builds. The summary names the build and its commit in place of the repository and the ref. --from with --ref is a usage error, and so is an id of another kind. A build of another repository than the one the command selects is a usage error before the confirmation, because the API deploys a build only to the app of its own repository. A build keeps its files for 7 days. The API refuses an older build with its own message: build the repository again.
$ district deploy --from hdep_01K4… --target shopfront --yes
build hbld_01K4… (9f1c2e4…)
hostname shopfront.ondistrict.net
environment production
✔ deployment hdep_01K5… succeeded
pinned https://hdep-01k5….ondistrict.net/ · serves this deployment only, not the site address
env production
https://shopfront.ondistrict.net/The command prints the repository, ref, target, and environment, then asks Deploy? [y/N]. --yes skips the question. Without a terminal and without --yes, the command stops because an agent cannot answer.
Arguments
| Argument | Description |
|---|---|
[<repository>] | The repository, by repo_ id or by slug. Without it, the clone in the current directory names one: its origin must be a District remote, which also names the environment. |
Flags
| Flag | Description | Default |
|---|---|---|
--target <slug> | The label of the target that serves the build. Not accepted with --new-preview. | the primary target |
--new-preview | Creates a preview target with the build. district-api generates its label, and it expires 7 days after its last successful deployment. Not accepted with --target. | false |
--ref <ref> | The commit or branch to build. Without it, the ref is the clone's HEAD object name. With a positional repository and no --ref, the build uses the repository's default branch. | the clone's HEAD |
--from <id> | Deploys the build of an existing deployment (hdep_…) or an existing build (hbld_…) instead of building. The new deployment uses the target's environment. Not accepted with --ref. | - |
--workspace <id> | The workspace of the repository, when the same slug names a repository in more than one workspace. | - |
--yes | Skips the "Deploy? [y/N]" question. A session with no terminal needs it. | false |
--json | Prints the build and the deployment as one JSON object on stdout, and no step or status lines. | false |
--production | --staging | --dev | The environment to act in. Pass one at most. Without a flag, the command uses the environment that the last district login recorded, or production. | - |
Output
$ district deploy --yes
build hbld_01K4… started (re-attach with: district build attach hbld_01K4…)
✔ checkout succeeded 3 s
✔ install succeeded 41 s
✔ build succeeded 96 s
✔ bundle succeeded 7 s
✔ export succeeded 2 s
✔ deployment hdep_01K4… succeeded
pinned https://hdep-01k4….ondistrict.net/ · serves this deployment only, not the site address
env production
https://shopfront.ondistrict.net/One line per finished build step and the deployment's final status go to stderr. The URL goes to stdout. In a terminal, a spinner carries the step and the deployment status in progress. An undecorated stderr gets one plain line per deployment status change instead.
The pinned line names the host that serves this deployment only. The env line names the deployment's environment: the wrangler.jsonc section whose bindings it uses, or - for a deployment made before environments existed. It is not the District environment that --staging selects, which the summary names as environment. When the target's environment changed after the deployment was made, the command also prints Environment change pending deployment.
After the build finishes, the command waits at most 15 minutes for the deployment row, which covers the whole retry budget of the build's deploy step. A build that produced neither a deployment nor a recorded deploy error in that time exits non-zero and names district target list. A recorded deploy error names district deploy --from <hbld_…>, which deploys the build that the step left behind.
The command then reads the deployment every 2 seconds for the first minute and every 10 seconds after that. It stops after 1 hour with deployment <id> did not finish; last status <status>.
On a read failure, the command exits non-zero and prints the --json object. While the build runs, the message names district build attach <id>. After the deployment exists, the message names the deployment, which continues to run after the CLI exits.
A deployment can succeed without becoming the target's current deployment: a newer deployment can finish first, or an operator can point the target at another deployment. The command compares the target's current deployment with this deployment. A deployment that becomes current and is later displaced gets the same report. The deployment exists, and no target serves it. The command exits non-zero and names both the deployment that the target serves and district target set-deployment, which repoints the target:
deployment hdep_01K4… succeeded but shopfront.ondistrict.net serves hdep_01K4…E. Point the hostname at it with: district target set-deployment shopfront hdep_01K4…--json prints { "build": …, "deployment": … } as one object on stdout. It prints no step or deployment status lines. The summary and the build <id> started line still go to stderr. Each outcome with a build prints this object, so a script can read .build.status for every build outcome. "deployment": null identifies an outcome with no deployment to print: a build that stopped before one existed, or a deployment that disappeared while the command read it. An outcome with no build, such as a declined confirmation or a repository that does not resolve, prints nothing.
Exit codes
| Exit code | Meaning |
|---|---|
| 0 | The target serves the deployment. |
| 1 | The build failed or errored, the confirmation was declined, the deploy step recorded an error, no deployment appeared, the deployment failed, a poll did not finish, the deployment did not become the target's current deployment, or the API cannot read or refuses what --from names. |
| 2 | The command line names no repository or target, HEAD is not pushed, no terminal can confirm, --from names an id of another kind or a build of another repository, or --from comes with --ref. |