Skip to content
Containly

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

  1. Open Profile > API tokens and click New token.
  2. Give it a name, for example github-actions shop. The audit log shows every action of the token as <your name> (Token: <name>).
  3. 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 and docker compose up -d for 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).
  4. Choose an expiry (30 days, 90 days, 1 year or none) and click Create token.
  5. 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

StatusMeaning
202Redeploy started (or already running), see statusUrl
401Token missing, wrong, expired or revoked, or the owner is disabled
403 token_scopeThe token lacks the permission for this route, or tokens are not allowed there
403The owner's role does not allow it (for example a viewer) or the token is limited to other stacks
404No stack with this name or ID
409Several stacks with this name: add "endpoint", or a backup or restore holds the stack
429Too many requests, wait a minute