Skip to content

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.

  • An app you have the developer role on. The source type (git, binary or zip) is chosen when the app is created and can’t be changed.
  • An API token for the scripted examples (Use the API with tokens).
Terminal window
PANEL=https://panel.example.com
H='Authorization: Bearer gsl_xxxxxxxxxxxxxxxx'
J='Content-Type: application/json'
  1. Create the app with "sourceType": "git", a gitUrl and, optionally, a gitBranch. With no branch, the repository’s default branch is built. Accepted URLs are https://, ssh://, git:// and user@host:path. Local paths and file:// are refused.

  2. 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": "…"}

    algorithm is ed25519, rsa-4096, rsa-2048, ecdsa-p256 or ecdsa-p384. Add the public key to the repository as a read-only deploy key, use an ssh:// or git@host:org/repo.git URL, 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.

  3. 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": true to 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.

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).

Terminal window
# 1. Upload: the raw file is the request body
ARTIFACT=$(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 upload
curl -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:

Terminal window
git archive --format=zip -o /tmp/src.zip HEAD
ARTIFACT=$(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\"}"

For git apps. You need the admin role.

  1. 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": "…"}
  2. 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).
  3. Push to the app’s branch. The delivery answers 202 {"deploymentId": …}. These are acknowledged with 200 {"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 get 401 invalid_signature.

Limits: 120 deliveries per minute per source, bodies up to 5 MB.

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).

Terminal window
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.

Terminal window
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}'