Skip to content

Apps API

Routes under /apps. Deployments, domains, cron jobs, metrics, logs and the webhook are on Deployments API and App resources API.

Method Path Permission Body / query Returns
GET /apps/ Authenticated Query projectId? {results}: apps you can see
POST /apps/ Developer on the project’s team Create body The app (status: "created")
GET /apps/metrics-summary Authenticated {results}: latest {appId, name, status, cpuPercent, memBytes, memoryMbLimit, cpuQuotaPercent, sampledAt} per running app, samples under 5 minutes old
GET /apps/limits Authenticated {cpuCores, maxCpuQuotaPercent}
GET /apps/{id} Viewer The app
PATCH /apps/{id} Developer (Admin on both teams to move across teams) Update body The app
DELETE /apps/{id} Admin {detail: "App removal requested."}

When to use: create an app once, then deploy it. PATCH changes settings that take effect on the next deploy. The exceptions are the static settings (staticSpa, staticRoot) and media settings (mediaEnabled, mediaMaxUploadMb), which re-render the Nginx vhost immediately without a rebuild. DELETE tears down the unit, vhost, firewall rule, Linux user and files, then the app’s rows.

Field Type Default Description
projectId int required Project to create the app in.
name string required Up to 60 characters.
sourceType string required git, binary or zip. Fixed after creation.
applicationId int Installed runtime to target. Preferred.
runtime string Runtime key (dart, go, rust, node, bun, python, celery, zig, binary, static), as an alternative to applicationId. The runtime must be installed.
deploymentMode string runtime’s default build_execute, direct_run or static_publish. Must be one the runtime supports.
exposeMode string web web, tcp, internal or process. Fixed after creation. Static apps are always web. Celery can’t be process.
internalPort int 1024 to 65535, unique on the host. Required except for static and process. Passed to the app as PORT.
publiclyReachable bool true for tcp Open the tcp app’s port in ufw.
gitUrl string https://, ssh://, git:// or user@host:path.
gitBranch string repo default Branch or tag.
sourceSubdir string repo root Subdirectory to build and run from.
deployKeyId int One of your SSH keys, for private repos.
buildCommand string runtime’s default A single command (no |, &, ;, $, <, > or backticks).
startCommand string runtime’s default Same character rules. Required for tcp, internal and process apps on python, node, bun and rust.
healthCheckPath string Path probed over HTTP after each deploy (web apps), for example /healthz.
memoryMbLimit int 256 32 to the host’s RAM in MB.
cpuQuotaPercent int 50 1 to cores × 100 (percent of one core).
tasksLimit int 100 16 to 16384.
dartVersion, goVersion, rustVersion, nodeVersion, bunVersion, pythonVersion string runtime default Toolchain version pin, such as 3.5.4, 22.12.0 or stable.
pythonMode string wsgi wsgi or asgi.
wsgiApp string Gunicorn app target, such as myproject.wsgi:application.
gunicornWorkers, gunicornThreads, gunicornTimeout int 4, 1, 120 Gunicorn tuning.
gunicornBind, gunicornExtraArgs string Bind override and extra flags.
celeryApp, celeryQueues, celeryExtraArgs string Celery app path, comma-separated queues, extra flags.
celeryWorkerCount, celeryConcurrency int 2, 4 Celery workers and concurrency per worker.
celeryBeatEnabled bool false Run Celery beat.
staticRoot string Directory to serve for static, such as dist.
staticSpa bool false Fall back to index.html.
mediaEnabled bool false Persistent shared/media, served at /media/.
mediaMaxUploadMb int 25 Nginx client_max_body_size.

Creation also checks the team quota.

Example

Terminal window
curl -sX POST https://panel.example.com/apps/ \
-H 'Authorization: Bearer gsl_xxxxxxxxxxxxxxxx' -H 'Content-Type: application/json' \
-d '{"projectId": 12, "name": "billing-api", "runtime": "dart", "sourceType": "git",
"gitUrl": "https://github.com/acme/billing-api.git", "internalPort": 4001,
"healthCheckPath": "/healthz"}'

Any subset of: name, projectId, gitUrl, gitBranch, sourceSubdir ("" clears it), buildCommand, startCommand, healthCheckPath, releasesToKeep (1 to 50, default 5), memoryMbLimit, cpuQuotaPercent, tasksLimit, the version pins, the Python, Gunicorn and Celery fields, staticRoot, staticSpa, mediaEnabled, mediaMaxUploadMb, deployKeyId, internalPort. sourceType and exposeMode can’t be changed.

The app is returned as a database row, so its keys are snake_case: id, project_id, name, slug, linux_user (app_<6 chars>), work_dir, internal_port, expose_mode, publicly_reachable, application_id, runtime, deployment_mode, source_type, git_url, git_branch, build_command, start_command, health_check_path, releases_to_keep, memory_mb_limit, cpu_quota_percent, tasks_limit, the runtime-specific settings, status, last_deployed_at, created_at and updated_at.

status is one of created, building, running, stopped, failed, crashed or deleting.

Method Path Permission Returns
POST /apps/{id}/start Developer {detail: "Start requested."}
POST /apps/{id}/restart Developer {detail: "Restart requested."}
POST /apps/{id}/stop Admin {detail: "Stop requested."}

When to use: these act on the existing build and unit. A restart doesn’t re-read env vars from the panel. Redeploy for that.

Method Path Permission Body Returns
POST /apps/{id}/exec Developer {command} (up to 4000 characters) {execId}

The command runs once as the app’s Linux user, in its directory, with its env vars (and the virtualenv activated for Python and Celery). Stream the output on /ws/apps/{id}/exec/{execId}. Use it for migrations and one-off maintenance.

Method Path Permission Body Returns
POST /apps/{id}/network Developer {publiclyReachable} The app

Only for tcp apps. Opens or closes ufw for the app’s port with no rebuild or restart. Re-sending the same value retries a failed reconcile.

Method Path Permission Body Returns
GET /apps/{id}/envs Viewer {results}: {id, app_id, name, value, is_secret, updated_at}. value is left out for secrets.
POST /apps/{id}/envs Developer {name, value?, isSecret?} The env var. Upserts by name. Leave out value to keep the stored one.
POST /apps/{id}/envs/bulk Developer {entries: [{name, value, isSecret?}, …]} {imported}
DELETE /apps/{id}/envs/{envId} Developer {detail}

Names must be identifiers ([A-Za-z_][A-Za-z0-9_]*), at most 200 characters. Values are encrypted at rest and written to the app’s .env on the next deploy. See Env vars and secrets.