Apps
The app catalogue, creating an app, starting and stopping it, and the subscription that follows its state.
An app is one deployable unit: a repository, an uploaded archive, a Docker image or a compose stack. Its settings have their own page, App settings; shipping it is on Deployments.
search(q) is not scoped to the active team
It answers with hits from every team the caller can reach, each carrying the team it lives in, so a client that has a name but not a team can find it and then work there (with X-Deplo-Team for a token, or the team argument of an MCP tool). A team the caller cannot enter right now contributes nothing. At most 50 hits per kind.
Reading
apps
Any signed-in principal. Returns [App!].
All apps in the active team, newest first.
| Argument | Type | Description |
|---|---|---|
q | String | Keep only the apps whose name, slug or id contains this, ignoring case and separators. Use search to look across teams. |
query {
apps(q: "shop") {
id
slug
name
status
latestDeployment { status createdAt }
}
}app
Any signed-in principal. Returns App.
One app by its team-scoped slug, or null.
| Argument | Type | Description |
|---|---|---|
slug | String! |
query {
app(slug: "shop") {
id
name
status
productionUrl
framework
deployments { id status createdAt }
}
}search
Any signed-in principal. Returns SearchResults.
Find anything by name, slug or id across EVERY team the caller can reach - the one read in Deplo that is not scoped to the active team. Each hit says which team it is in. Separators and case are ignored, so "better auth" finds better-auth-docs. Hits from the active team come first, then the closest match. Teams the caller cannot enter right now (an unmet two-factor policy, a narrowed token) contribute nothing rather than failing the search.
| Argument | Type | Description |
|---|---|---|
kinds | [SearchKind!] | Which kinds to look through. Omitted means all ten - and each is a separate read PER TEAM, so name the ones you need. |
q | String! | Part of a name, slug or id. Blank finds nothing. |
query {
search(q: "shop", kinds: [app, database]) {
apps { id slug name team { slug } }
databases { id name team { slug } }
}
}appRuntime
Any signed-in principal. Returns AppRuntime.
Live container state for an app, straight from the owning agent - the truth behind the stored status. Polled by the app's status badge.
| Argument | Type | Description |
|---|---|---|
appId | String! |
query {
appRuntime(appId: "prj_9f1c2ab7d3e4f5a6") {
running
total
unhealthy
unreachable
}
}overviewAppStates
Any signed-in principal. Returns [OverviewAppState!]!.
Batch runtime state for the visible Overview apps. Unknown or inaccessible IDs are omitted.
| Argument | Type | Description |
|---|---|---|
appIds | [ID!]! |
query {
overviewAppStates(appIds: [["prj_9f1c2ab7d3e4f5a6"]]) {
status
appId
neverDeployed
}
}appTransferInfo
Needs move_apps. Returns AppTransferInfo.
What transferring this app to another team would change, and which of the viewer's other teams could take it.
| Argument | Type | Description |
|---|---|---|
appId | String! |
query {
appTransferInfo(appId: "prj_9f1c2ab7d3e4f5a6") {
appName
targets {
id
name
serverAvailable
}
}
}Creating
createApp takes the whole first configuration in one input. The new-app wizard runs detectRepoFramework while you pick a repository and composeNameClashes before it submits a stack.
createApp
Needs create_apps. Returns App.
Create an app from a repository, an uploaded archive, a Docker image or a compose stack, with its first build settings, variables, domains and mounts. Deploys immediately unless the source needs an upload first.
| Argument | Type | Description |
|---|---|---|
input | CreateAppInput! | |
input.autoDeploy | Boolean | |
input.autoDomain | String | |
input.autoDomainPath | String | |
input.build | BuildConfigInput | |
input.buildServerId | String | Where the app COMPILES, when that is not where it runs. Omitted is Automatic - a build-only server if the fleet has one. |
input.compose | String | |
input.composePort | Int | |
input.composeService | String | |
input.composeUpArgs | String | Extra flags appended to docker compose up for a compose stack. Validated against the same allow-list the app's settings use. |
input.deploy | Boolean | Whether to start the first deployment. Defaults to true. |
input.dockerImage | String | |
input.env | [AppEnvInput!] | |
input.environmentId | String | |
input.extraDomains | [ExtraDomainInput!] | |
input.folderId | String | |
input.logo | String | |
input.logoFromTemplate | Boolean | The logo above is a template's own. Deplo reads it once for the plate that keeps a monochrome mark visible on both themes. |
input.mounts | [MountInput!] | |
input.name | String! | |
input.projectId | String | |
input.renameClashes | Boolean | Rename a service whose name a neighbour on the destination network already answers to (db becomes db-2), carrying its references, instead of refusing the stack. See composeNameClashes. |
input.repo | GitRepoInput | |
input.serverId | String | |
input.sharedVarIds | [String!] | Shared variables to link to the new app, so its FIRST deploy already carries them (ADR-0012: linking is the injection, and it is opt-in). |
input.source | DeploySource! | One of COMPOSE, DOCKER_IMAGE, GIT, GITHUB, UPLOAD. |
mutation {
createApp(
input: {
name: "shop"
source: GITHUB
repo: { provider: "github", repo: "acme/shop", branch: "main", url: "https://github.com/acme/shop", installationId: "ghi_9f1c2ab7d3e4f5a6" }
build: { buildMethod: "nixpacks", port: 3000 }
env: [{ key: "NODE_ENV", value: "production" }]
autoDeploy: true
}
) {
id
slug
name
status
productionUrl
}
}createAppFromTemplate
Needs create_apps. Returns App.
Create an app from the public template catalog. deploy: true builds it right away.
| Argument | Type | Description |
|---|---|---|
input | CreateAppFromTemplateInput! | |
input.deploy | Boolean | Request the first deployment. Defaults to false; a token also needs deploy_apps. |
input.environmentId | String | |
input.folderId | String | |
input.name | String | The new App name. Omitted uses the template name. |
input.projectId | String | |
input.serverId | String | |
input.templateSlug | String! | The catalog template slug from templateVariants. |
input.variantSlug | String | The variant slug. Omitted uses the "default" variant. |
mutation {
createAppFromTemplate(
input: { templateSlug: "umami", name: "analytics", deploy: true }
) {
id
slug
name
status
productionUrl
}
}templateVariants
Any signed-in principal. Returns [TemplateVariantSummary!].
List the compact deployable variants in the public template catalog.
| Argument | Type | Description |
|---|---|---|
category | String | Filter by the category slug. |
q | String | Keep variants whose template, variant, category or description matches. |
query {
templateVariants(q: "analytics") {
templateSlug
variantSlug
name
category
}
}refreshTemplates
Any signed-in principal. Returns Boolean.
Drop the cached template catalog so the next read hits the catalog service.
mutation {
refreshTemplates
}composeNameClashes
Needs create_apps. Returns [ComposeNameClash!].
The service names a stack would share with something already on the network it lands on - what createApp refuses it over, asked before creating so the caller can pass renameClashes instead. Empty when nothing clashes.
| Argument | Type | Description |
|---|---|---|
input | ComposeNameClashesInput! | |
input.compose | String! | |
input.environmentId | String | |
input.folderId | String | |
input.projectId | String | |
input.serverId | String |
query {
composeNameClashes(
input: { compose: "services:\n web:\n image: nginx\n", serverId: "srv_9f1c2ab7d3e4f5a6" }
) {
name
owner
renamedTo
}
}detectRepoFramework
Needs create_apps. Returns RecognizedFramework.
Read a GitHub repository before an app exists for it - the framework and the build command the new-app wizard prefills while you pick a repo. Null when there is nothing to read at all: a build method other than Nixpacks / Railpack (the only ones this applies to), a repository Deplo can't read, or one with neither a framework nor a script. Reads only; the app's first deploy re-derives and stores the framework.
| Argument | Type | Description |
|---|---|---|
branch | String | Branch to read; empty => the repository's default branch. |
buildMethod | String! | The build method the app will use, e.g. "nixpacks". |
installationId | String | GitHub App installation to read a private repo through. Ignored unless it belongs to the active team. |
repo | String! | The repository as "owner/name". |
rootDirectory | String | Build sub-directory, for a monorepo. |
url | String | Its clone URL, when the caller has one (else derived). |
query {
detectRepoFramework(repo: "acme/shop", buildMethod: "nixpacks", installationId: "ghi_9f1c2ab7d3e4f5a6") {
id
name
buildCommand
startCommand
defaultPort
}
}Running and stopping
startApp
Needs control_apps. Returns App.
Start a stopped app from its last deployment.
| Argument | Type | Description |
|---|---|---|
id | String! |
mutation {
startApp(id: "prj_9f1c2ab7d3e4f5a6") {
id
slug
name
status
productionUrl
}
}stopApp
Needs control_apps. Returns App.
Stop the app's containers without deleting anything.
| Argument | Type | Description |
|---|---|---|
id | String! |
mutation {
stopApp(id: "prj_9f1c2ab7d3e4f5a6") {
id
slug
name
status
productionUrl
}
}reloadApp
Needs control_apps. Returns String.
Re-apply the app's routing (domains + basic auth) to the running stack without a rebuild. Returns 'rerouted', 'unchanged', or 'deferred'.
| Argument | Type | Description |
|---|---|---|
id | String! |
mutation {
reloadApp(id: "prj_9f1c2ab7d3e4f5a6")
}rebuildApp
Needs deploy_apps. Returns App.
Rebuild the image from the current source and REPLACE the running container, even if nothing about the stack changed. Volumes, domains and data are untouched.
| Argument | Type | Description |
|---|---|---|
id | String! |
mutation {
rebuildApp(id: "prj_9f1c2ab7d3e4f5a6") {
id
slug
name
status
productionUrl
}
}bulkAppAction
Needs control_apps. Returns BulkAppActionResult.
Start, stop or restart EVERY app in one folder (its whole subtree) or one project (every environment). Gated again per app, so it only touches the ones the caller may control; one failure never stops the rest. Give exactly one of folderId / projectId.
| Argument | Type | Description |
|---|---|---|
action | BulkAppAction! | One of restart, start, stop. |
folderId | ID | |
projectId | ID |
mutation {
bulkAppAction(action: restart, folderId: "fld_9f1c2ab7d3e4f5a6") {
ok
failed
}
}bulkRedeployApps
Needs deploy_apps. Returns BulkAppActionResult.
Redeploy EVERY app in one folder (its whole subtree) or one project (every environment): the bulk twin of redeploy. Give exactly one of folderId / projectId.
| Argument | Type | Description |
|---|---|---|
folderId | ID | |
projectId | ID |
mutation {
bulkRedeployApps(projectId: "prc_9f1c2ab7d3e4f5a6") {
ok
failed
}
}Renaming, moving and deleting
renameApp
Needs configure_apps. Returns App.
Rename an app. The slug, and with it the auto domain, stays as it is.
| Argument | Type | Description |
|---|---|---|
id | String! | |
name | String! |
mutation {
renameApp(id: "prj_9f1c2ab7d3e4f5a6", name: "shop") {
id
slug
name
status
productionUrl
}
}reorderApps
Needs manage_team, or instance admin. Returns Boolean.
Set the team-wide display order of apps in Overview.
| Argument | Type | Description |
|---|---|---|
appIds | [ID!]! |
mutation {
reorderApps(appIds: [["prj_9f1c2ab7d3e4f5a6"]])
}transferAppToTeam
Needs move_apps. Returns Boolean.
Hand this app over to another team the viewer belongs to. A live app restarts briefly on that team's network (nothing is rebuilt); it leaves its folder/project, loses its shared-variable links, backup schedules and cron jobs, and keeps its GitHub connection only if the destination team has its own installation on that account. Returns true.
| Argument | Type | Description |
|---|---|---|
appId | String! | |
teamId | String! |
mutation {
transferAppToTeam(
appId: "prj_9f1c2ab7d3e4f5a6"
teamId: "team_9f1c2ab7d3e4f5a6"
)
}deleteApp
Needs delete_apps. Returns Boolean.
Delete the app. Returns as soon as the deletion is RECORDED - from that moment the app is refused by every gate and gone from the product, and the stack teardown finishes on the host behind the response. Its stored backups are deleted first; if one cannot be, nothing is deleted.
| Argument | Type | Description |
|---|---|---|
id | String! |
mutation {
deleteApp(id: "prj_9f1c2ab7d3e4f5a6")
}deleteApps
Needs delete_apps. Returns Int.
Bulk-delete several apps. Returns how many were recorded as deleted; the bounded-concurrency teardown runs behind the response.
| Argument | Type | Description |
|---|---|---|
ids | [ID!]! |
mutation {
deleteApps(ids: ["ids"])
}Logo and framework
updateAppLogo
Needs configure_apps. Returns App.
Set the app's logo (an image data-URI), or clear it with null.
| Argument | Type | Description |
|---|---|---|
id | String! | |
logo | String |
mutation {
updateAppLogo(id: "prj_9f1c2ab7d3e4f5a6", logo: "data:image/png;base64,…") {
id
logo
}
}detectAppLogo
Needs configure_apps. Returns App.
Auto-detect a favicon from the app's own files - its GitHub repo, its uploaded archive, or (for a compose stack) its files dir on its server, and set it as the logo. Errors if none is found.
| Argument | Type | Description |
|---|---|---|
id | String! |
mutation {
detectAppLogo(id: "prj_9f1c2ab7d3e4f5a6") {
id
slug
name
status
productionUrl
}
}setAppFramework
Needs configure_apps. Returns App.
Correct the framework Deplo recognised in this app's source (a catalog id such as "vite"), or pass null to go back to trusting detection. The correction survives every later deploy's re-detection.
| Argument | Type | Description |
|---|---|---|
framework | String | |
id | String! |
mutation {
setAppFramework(id: "prj_9f1c2ab7d3e4f5a6", framework: "vite") {
id
framework
}
}Subscription
appStatus
Any signed-in principal. Returns App.
Emits the app whenever its status (power / deployment) changes. Fires once immediately with the current snapshot, then on every change.
| Argument | Type | Description |
|---|---|---|
slug | String! |
subscription {
appStatus(slug: "shop") {
id
status
latestDeployment { id status }
}
}See also
Did this page help you?