Docker ManagerAlburujITS

Base URL

All endpoints live under http://dockerhost2.sitesv2.alburujits.com/api/v1. Every snippet below already points at this deployment.

Authentication

Every call needs an API token in an Authorization header:

Authorization: Bearer dkm_1_paste-your-token-here

Create one from the API Tokens screen while signed in. The full token value is displayed once, at creation time — it is encrypted at rest afterwards and cannot be shown again. Lose it and you issue a new one.

A token carries exactly the access its owner has, resolved fresh on every call. It can never do more than the person who created it, and revoking the token or removing the owner's workspace grant takes effect immediately.

Identifying a container

Actions are keyed on this app's own stable container id (a GUID), not the Docker container id — Docker's id changes every time a container is recreated, ours does not. If you only know the host and the container's name, resolve the id once with the lookup endpoint and reuse it.

Endpoints

Method Path Needs What it does Returns
GET /api/v1/hosts/{hostId}/containers/{name}/id read Resolves a host id + Docker container name to the stable container id every other endpoint takes. Call this once and cache the result — the id survives recreates, the Docker id does not. { "managedContainerId": "3f2a9c56-1f3d-4b0e-9b1a-7d2c5e8a4f10" }
GET /api/v1/containers/{id}/status read Current name, host, workspace, image and run state, as of the last poll or action. { "id": "3f2a9c56-…", "name": "my-app", "host": "prod-1", "workspace": "Web", "image": "nginx:latest", "state": "Running" }
GET /api/v1/containers/{id}/logs?tail=200 read Container logs. tail accepts a line count or "all". Returns one blob of text, not a stream. { "text": "2026-08-17T09:12:33Z starting…" }
POST /api/v1/containers/{id}/start write Starts the container. { "ok": true }
POST /api/v1/containers/{id}/stop write Stops the container. { "ok": true }
POST /api/v1/containers/{id}/restart write Restarts the container. { "ok": true }
POST /api/v1/containers/{id}/pull-latest write Pulls the container's image again and recreates it from its live config, keeping its identity, name and role assignments. { "ok": true }

"Needs" is the access the token's owner must have in the container's workspace: read for a Viewer-equivalent grant, write for an Operator-equivalent one. The WebApiReadOnly and WebApiFull roles exist for exactly this — they authorize API calls without unlocking the browser UI.

Examples

# ---------------------------------------------------------------
# 1. Your token. Create one from the API Tokens screen while signed
#    in — the full value is shown once, at creation time only.
# ---------------------------------------------------------------
$Token   = 'dkm_1_paste-your-token-here'
$Base    = 'http://dockerhost2.sitesv2.alburujits.com'
$Headers = @{ Authorization = "Bearer $Token" }

# ---------------------------------------------------------------
# 2. Resolve a container's stable id (host id 1, container "my-app")
# ---------------------------------------------------------------
$lookup = Invoke-RestMethod -Uri "$Base/api/v1/hosts/1/containers/my-app/id" -Headers $Headers
$Id = $lookup.managedContainerId
$Id

# ---------------------------------------------------------------
# 3. Read state
# ---------------------------------------------------------------
$status = Invoke-RestMethod -Uri "$Base/api/v1/containers/$Id/status" -Headers $Headers
$status | Format-List

# Last 500 log lines
(Invoke-RestMethod -Uri "$Base/api/v1/containers/$Id/logs?tail=500" -Headers $Headers).text

# ---------------------------------------------------------------
# 4. Act on it (POST with no body)
# ---------------------------------------------------------------
Invoke-RestMethod -Method Post -Uri "$Base/api/v1/containers/$Id/restart" -Headers $Headers

# Pull the newest image and recreate
Invoke-RestMethod -Method Post -Uri "$Base/api/v1/containers/$Id/pull-latest" -Headers $Headers

# ---------------------------------------------------------------
# Restart anything that isn't running, end to end
# ---------------------------------------------------------------
foreach ($name in @('my-app', 'my-worker')) {
    $id = (Invoke-RestMethod -Uri "$Base/api/v1/hosts/1/containers/$name/id" -Headers $Headers).managedContainerId
    $state = (Invoke-RestMethod -Uri "$Base/api/v1/containers/$id/status" -Headers $Headers).state
    if ($state -ne 'Running') {
        Write-Host "$name is $state - restarting"
        Invoke-RestMethod -Method Post -Uri "$Base/api/v1/containers/$id/restart" -Headers $Headers | Out-Null
    }
}

# If the server uses a self-signed certificate, add:
#   -SkipCertificateCheck        (PowerShell 7+)
# pip install requests
import requests

# ---------------------------------------------------------------
# 1. Your token. Create one from the API Tokens screen while signed
#    in — the full value is shown once, at creation time only.
# ---------------------------------------------------------------
TOKEN = "dkm_1_paste-your-token-here"
BASE = "http://dockerhost2.sitesv2.alburujits.com"
HEADERS = {"Authorization": f"Bearer {TOKEN}"}


def resolve_id(host_id: int, name: str) -> str:
    # Host id + Docker name -> the stable container id.
    response = requests.get(f"{BASE}/api/v1/hosts/{host_id}/containers/{name}/id", headers=HEADERS)
    response.raise_for_status()
    return response.json()["managedContainerId"]


def status(container_id: str) -> dict:
    response = requests.get(f"{BASE}/api/v1/containers/{container_id}/status", headers=HEADERS)
    response.raise_for_status()
    return response.json()


def logs(container_id: str, tail: str = "200") -> str:
    response = requests.get(
        f"{BASE}/api/v1/containers/{container_id}/logs",
        params={"tail": tail}, headers=HEADERS)
    response.raise_for_status()
    return response.json()["text"]


def action(container_id: str, verb: str) -> dict:
    # verb: start | stop | restart | pull-latest
    response = requests.post(f"{BASE}/api/v1/containers/{container_id}/{verb}", headers=HEADERS)
    response.raise_for_status()
    return response.json()


# ---------------------------------------------------------------
# Restart anything that isn't running
# ---------------------------------------------------------------
if __name__ == "__main__":
    for container_name in ("my-app", "my-worker"):
        container_id = resolve_id(1, container_name)
        state = status(container_id)["state"]
        if state != "Running":
            print(f"{container_name} is {state} - restarting")
            action(container_id, "restart")

# A 403 means the token's owner has no write role in that container's
# workspace; a 502 means Docker itself refused the operation, and the
# response body carries the daemon's error message.
# ---------------------------------------------------------------
# 1. Your token. Create one from the API Tokens screen while signed
#    in — the full value is shown once, at creation time only.
# ---------------------------------------------------------------
TOKEN="dkm_1_paste-your-token-here"
BASE="http://dockerhost2.sitesv2.alburujits.com"

# ---------------------------------------------------------------
# 2. Resolve a container's stable id (host id 1, container "my-app")
# ---------------------------------------------------------------
curl -s -H "Authorization: Bearer $TOKEN" \
     "$BASE/api/v1/hosts/1/containers/my-app/id"
# -> {"managedContainerId":"3f2a9c56-1f3d-4b0e-9b1a-7d2c5e8a4f10"}

ID="3f2a9c56-1f3d-4b0e-9b1a-7d2c5e8a4f10"

# ---------------------------------------------------------------
# 3. Read state
# ---------------------------------------------------------------
curl -s -H "Authorization: Bearer $TOKEN" "$BASE/api/v1/containers/$ID/status"

# Last 500 log lines
curl -s -H "Authorization: Bearer $TOKEN" "$BASE/api/v1/containers/$ID/logs?tail=500"

# ---------------------------------------------------------------
# 4. Act on it (POST with no body)
# ---------------------------------------------------------------
curl -s -X POST -H "Authorization: Bearer $TOKEN" "$BASE/api/v1/containers/$ID/restart"

# Pull the newest image and recreate
curl -s -X POST -H "Authorization: Bearer $TOKEN" "$BASE/api/v1/containers/$ID/pull-latest"

# Add -i (or -w '%{http_code}') to see the status code — a 403 means
# the token's owner has no write role in that container's workspace.

A workflow that builds and pushes your image, then calls pull-latest so this app pulls it and recreates the container. Save it as .github/workflows/deploy.yml. This server must be reachable from GitHub's runners (or use a self-hosted runner).

# .github/workflows/deploy.yml
#
# After a new image is pushed to your registry, ask Docker Manager to
# pull the latest image and recreate the container.
#
# One-time setup in the GitHub repo (Settings > Secrets and variables > Actions):
#   Secret   DOCKER_MANAGER_TOKEN  an API token from the API Tokens screen
#                                  (its owner needs a write role on the container)
#   Variable DOCKER_MANAGER_HOST_ID  the host id, e.g. 1
#   Variable DOCKER_MANAGER_CONTAINER  the Docker container name, e.g. my-app
name: Deploy

on:
  push:
    branches: [main]
  workflow_dispatch:

env:
  DOCKER_MANAGER_URL: http://dockerhost2.sitesv2.alburujits.com
  IMAGE: ghcr.io/${{ github.repository }}:latest

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      # Replace with your own build if it differs.
      - uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          tags: ${{ env.IMAGE }}

  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: Pull latest image and restart the container
        env:
          TOKEN: ${{ secrets.DOCKER_MANAGER_TOKEN }}
          HOST_ID: ${{ vars.DOCKER_MANAGER_HOST_ID }}
          NAME: ${{ vars.DOCKER_MANAGER_CONTAINER }}
        run: |
          set -euo pipefail
          auth="Authorization: Bearer $TOKEN"

          # 1. Resolve the stable container id from host id + name.
          id=$(curl -sS --fail-with-body -H "$auth" \
            "$DOCKER_MANAGER_URL/api/v1/hosts/$HOST_ID/containers/$NAME/id" \
            | jq -r .managedContainerId)

          # 2. Pull the newest image and recreate the container
          #    (it keeps its name, id and role assignments).
          curl -sS --fail-with-body -X POST -H "$auth" \
            "$DOCKER_MANAGER_URL/api/v1/containers/$id/pull-latest"

          # 3. Check it came back up (state is "Running" when healthy).
          for attempt in 1 2 3 4 5 6; do
            state=$(curl -sS --fail-with-body -H "$auth" \
              "$DOCKER_MANAGER_URL/api/v1/containers/$id/status" | jq -r .state)
            echo "State: $state"
            [ "$state" = "Running" ] && exit 0
            sleep 5
          done
          echo "Container is not running after the update" >&2
          exit 1

# To only restart (no new image), POST to .../containers/$id/restart instead.
# A 403 means the token's owner has no write role in that container's workspace.

Response codes

CodeMeaningWhat to do
200 Success. Action endpoints return { "ok": true }. —
401 Missing, malformed, revoked or unknown token. Check the Authorization header and that the token hasn't been revoked.
403 Token is valid, but its owner lacks the needed role in that container's workspace. Ask an Admin for the right grant on that workspace or container.
400 The container has no known Docker id yet. Run Discover containers on that host once from the Hosts screen.
404 No container with that id, or no container by that name on that host. Re-resolve the id; the container may have been removed.
502 Docker itself refused or the host is unreachable. The response body's error field carries the daemon's own message.

Notes worth knowing

  • Action endpoints take no request body — POST with an empty body.
  • state is the last known state from the background poller or the last action, not a live inspect at call time.
  • logs returns a single text blob; there is no streaming/follow endpoint.
  • pull-latest recreates the container from its live configuration, keeping its name, id and role assignments. It is not a rolling update — the container is briefly down.
  • There is no rate limiting. A tight polling loop hits the Docker daemon on every call.