API tokens and CI redeploy
Since Containly 2.7.3 personal API tokens let GitHub Actions, Gitea or Forgejo Actions and other pipelines redeploy a stack with one curl, and Prometheus read the metrics. Free. Scopes, security, job status and ready examples.
Containly 2.7.3 adds personal API tokens and a redeploy API for CI pipelines. Both are Free.
Create a token
- Open Profile > API tokens and click New token.
- Give it a name, for example
github-actions shop. The audit log shows every action of the token as<your name> (Token: <name>). - Pick only the permissions the pipeline needs:
- Read the API: lists of hosts, containers, stacks, images, updates and backups (read only).
- Redeploy stacks: git pull for Git stacks, pull images anddocker compose up -dfor a stack. Only admins can create such tokens. Optionally limit the token to the stacks this pipeline deploys.
- Prometheus metrics: read/metrics(see the Prometheus guide). - Choose an expiry (30 days, 90 days, 1 year or none) and click Create token.
- Copy the token now. It is shown only once; Containly stores just a fingerprint of it. Store it as a secret in your CI (for example
DEPLOY_TOKEN).
The list shows when and from which IP each token was last used. Revoke stops a token at once. Admins see and revoke the tokens of every user under Settings > Integrations.
Security in short
- A token works only in the
Authorization: Bearer <token>header, only on the API routes its permission allows, never for signing in, consoles, WebSockets, users, settings or tokens themselves. - A token never does more than your account: when you become a viewer, are disabled or deleted, your redeploy token stops working.
- Requests are rate limited per token (at most 10 redeploys per minute).
- A redeploy runs the compose files that are in the stack folder (and in the Git repository for Git stacks). Whoever can push to that repository can change what runs: protect the branch.
Redeploy a stack
By name (the stack folder name; add "endpoint" with the host ID when two hosts have a stack with the same name):
curl -fsS -X POST \
-H "Authorization: Bearer $DEPLOY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"shop"}' \
https://containly.example.com/api/stacks/redeploy
Or by stack ID (POST /api/stacks/<id>/redeploy, the ID is in the browser address when the stack is open).
Containly answers 202 with a job:
{"job":{"id":"job_MhIaXDMjzVWb","status":"queued","stackName":"shop", "...": "..."},"statusUrl":"/api/jobs/job_MhIaXDMjzVWb"}
What the job does: for a Git stack git pull, then docker compose pull and docker compose up -d --remove-orphans with all compose files of the stack (override files included). Poll GET /api/jobs/<id> until status is ok or error; output holds the end of the compose output, error the reason. A second request while the same stack is being redeployed returns the running job.
GitHub Actions
Store the token as a repository secret DEPLOY_TOKEN and the address as a variable DEPLOY_URL. Your Containly must be reachable from the GitHub runner (or use a self-hosted runner in your network).
name: deploy
on:
push:
branches: [main]
jobs:
redeploy:
runs-on: ubuntu-latest
steps:
- name: Redeploy the stack in Containly
env:
TOKEN: ${{ secrets.DEPLOY_TOKEN }}
URL: ${{ vars.DEPLOY_URL }}
run: |
job=$(curl -fsS -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"name":"shop"}' "$URL/api/stacks/redeploy" | jq -r .job.id)
for i in $(seq 1 60); do
st=$(curl -fsS -H "Authorization: Bearer $TOKEN" "$URL/api/jobs/$job" | jq -r .job.status)
echo "status: $st"
[ "$st" = ok ] && exit 0
[ "$st" = error ] && { curl -fsS -H "Authorization: Bearer $TOKEN" "$URL/api/jobs/$job" | jq -r .job.error; exit 1; }
sleep 5
done
exit 1
Gitea and Forgejo Actions
The same workflow works in Gitea and Forgejo Actions (file .gitea/workflows/deploy.yaml or .forgejo/workflows/deploy.yaml). Store DEPLOY_TOKEN under the repository's Settings > Actions > Secrets and DEPLOY_URL under Variables. With a runner in your own network, DEPLOY_URL can be the LAN address, for example http://10.0.0.5:8420.
Reading data
A token with Read the API can call for example GET /api/stacks, GET /api/containers, GET /api/updates or GET /api/hosts/metrics with the same header. GET /api/auth/me shows which user a token belongs to.
Answers you may see
| Status | Meaning |
|---|---|
| 202 | Redeploy started (or already running), see statusUrl |
| 401 | Token missing, wrong, expired or revoked, or the owner is disabled |
403 token_scope | The token lacks the permission for this route, or tokens are not allowed there |
| 403 | The owner's role does not allow it (for example a viewer) or the token is limited to other stacks |
| 404 | No stack with this name or ID |
| 409 | Several stacks with this name: add "endpoint", or a backup or restore holds the stack |
| 429 | Too many requests, wait a minute |