API reference
How to drive this Docker Manager from a script — start, stop, restart, pull-latest, read status and logs, from PowerShell, Python or curl. This page is public; the API itself is not.
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.
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
| Code | Meaning | What 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.
stateis the last known state from the background poller or the last action, not a live inspect at call time.logsreturns a single text blob; there is no streaming/follow endpoint.pull-latestrecreates 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.