Deploy from git, a binary or a ZIP
This guide covers the three deployment sources: building from git on the server, uploading a binary you built elsewhere, and uploading a ZIP of the source. It also sets up push-to-deploy webhooks with commit statuses.
Prerequisites
Section titled “Prerequisites”- An app you have the
developerrole on. The source type (git,binaryorzip) is chosen when the app is created and can’t be changed. - An API token for the scripted examples (Use the API with tokens).
PANEL=https://panel.example.comH='Authorization: Bearer gsl_xxxxxxxxxxxxxxxx'J='Content-Type: application/json'-
Create the app with
"sourceType": "git", agitUrland, optionally, agitBranch. With no branch, the repository’s default branch is built. Accepted URLs arehttps://,ssh://,git://anduser@host:path. Local paths andfile://are refused. -
For a private repository, give the panel an SSH deploy key. Generate one (the private key is returned once and also stored for clones):
Terminal window curl -sX POST $PANEL/me/security/ssh-keys/generate -H "$H" -H "$J" \-d '{"name": "billing-api deploy", "algorithm": "ed25519"}'# → {"key": {"id": 3, "public_key": "ssh-ed25519 AAAA…", …}, "privateKey": "…"}algorithmised25519,rsa-4096,rsa-2048,ecdsa-p256orecdsa-p384. Add the public key to the repository as a read-only deploy key, use anssh://orgit@host:org/repo.gitURL, and point the app at the key:Terminal window curl -sX PATCH $PANEL/apps/42 -H "$H" -H "$J" -d '{"deployKeyId": 3}'You can only use keys you own.
-
Deploy the branch head, or a specific commit:
Terminal window curl -sX POST $PANEL/apps/42/deployments/ -H "$H" -H "$J" \-d '{"sourceType": "git"}'curl -sX POST $PANEL/apps/42/deployments/ -H "$H" -H "$J" \-d '{"sourceType": "git", "gitCommitSha": "4f2a9c1"}'Add
"forceRebuild": trueto skip the dependency and build cache.
To override the runtime’s build step, set buildCommand on the app. It
must be a single command: the agent refuses |, &, ;, $, <, > and
backticks, so you can’t chain steps with &&. Put multi-step builds in a
script in the repository and call that, or build in CI and deploy a binary.
For a monorepo, set sourceSubdir (for example services/billing) on the app
so the build and run happen in that directory. PATCH it with "" to go
back to the repository root.
Binary
Section titled “Binary”Use this to build in CI and ship only the executable. The upload must be an
ELF binary or a #! script, at most 50 MB. It must run on the server’s
architecture (x64 or arm64).
# 1. Upload: the raw file is the request bodyARTIFACT=$(curl -sX POST $PANEL/apps/42/deployments/artifacts -H "$H" \ -H 'Content-Type: application/octet-stream' \ --data-binary @build/app | jq -r .artifactId)
# 2. Deploy that uploadcurl -sX POST $PANEL/apps/42/deployments/ -H "$H" -H "$J" \ -d "{\"sourceType\": \"binary\", \"artifactId\": \"$ARTIFACT\"}"The binary is installed as <workDir>/current/app and started from there,
unless you set a startCommand. For a Dart app, compile it in CI with
dart compile exe bin/server.dart -o build/app on the same OS and
architecture as the server.
The panel keeps the 5 most recent uploads per app. Rollbacks use kept releases, not uploads.
Same two calls, with a ZIP of the source tree. The archive is unpacked and built with the app’s runtime, exactly like a git checkout:
git archive --format=zip -o /tmp/src.zip HEADARTIFACT=$(curl -sX POST $PANEL/apps/42/deployments/artifacts -H "$H" \ -H 'Content-Type: application/octet-stream' \ --data-binary @/tmp/src.zip | jq -r .artifactId)curl -sX POST $PANEL/apps/42/deployments/ -H "$H" -H "$J" \ -d "{\"sourceType\": \"zip\", \"artifactId\": \"$ARTIFACT\"}"Push-to-deploy webhooks
Section titled “Push-to-deploy webhooks”For git apps. You need the admin role.
-
Create the webhook. The secret is returned only here. Calling the route again rotates it.
Terminal window curl -sX POST $PANEL/apps/42/webhook -H "$H"# → {"path": "/webhooks/apps/42", "branch": "main", "secret": "…"} -
In the repository settings, add a webhook for push events:
- URL:
https://panel.example.com/webhooks/apps/42 - Content type:
application/json - Secret: the value above. GitHub, Gitea and Forgejo sign the body
(
X-Hub-Signature-256). For GitLab, paste it as the secret token (X-Gitlab-Token).
- URL:
-
Push to the app’s branch. The delivery answers
202 {"deploymentId": …}. These are acknowledged with200 {"ignored": "…"}and don’t deploy: pings, other events, tags, other branches, deleted branches, redelivered delivery ids, and pushes while a deployment is still queued (the queued build will clone the newest head anyway). A bad signature, an unknown app or a removed webhook all get401 invalid_signature.
Limits: 120 deliveries per minute per source, bodies up to 5 MB.
Commit statuses
Section titled “Commit statuses”To see deploy results on each commit, give the panel a token that can write
commit statuses: “Commit statuses: write” (GitHub fine-grained token), api
(GitLab) or write:repository (Gitea, Forgejo).
curl -sX PUT $PANEL/apps/42/webhook/status-token -H "$H" -H "$J" \ -d '{"token": "<repo-host-token>"}'Each push-triggered deployment is reported as gisila-panel/deploy: pending
while queued, building and deploying, then success or failure. Set
PANEL_PUBLIC_URL in /etc/gisila/.env (for example
https://panel.example.com) so each status links to the app’s page. The token
is stored encrypted and never returned. DELETE …/webhook/status-token turns
statuses off.
Verify it works
Section titled “Verify it works”curl -s $PANEL/apps/42/deployments/ -H "$H" \ | jq '.results[0] | {id, status, source_type, git_commit_sha, failure_reason}'
curl -s $PANEL/apps/42/webhook -H "$H" \ | jq '{configured, enabled, lastDeliveryAt, lastDeliveryResult}'