REST API
Access Drydock state and trigger actions using the HTTP REST API.
You can access drydock state and trigger actions using the HTTP REST API.
By default, the API is enabled and exposed on port 3000. You can override this behaviour using environment variables.
API Versioning
Starting in v1.4.0, the canonical API base path is /api/v1/*. The unversioned /api/* path remains as a backward-compatible alias during migration.
| Path | Status | Description |
|---|---|---|
/api/v1/* | Canonical | Versioned API path — use this for new integrations |
/api/* | Deprecated alias | Planned for removal in v2.0.0 |
Both paths return identical responses. Third-party integrations should migrate to /api/v1/* at their convenience.
Error Contract
All error responses (HTTP 4xx/5xx) return a consistent JSON structure:
{
"error": "Container not found"
}| Field | Type | Description |
|---|---|---|
error | string | Human-readable error message (derived from the HTTP status when not explicitly set) |
details | object | Optional contextual metadata (present only when additional context is available) |
Authentication
When authentication is enabled, all API endpoints require a valid session (cookie-based via Basic or OIDC login).
For cross-domain OIDC providers, keep DD_SERVER_COOKIE_SAMESITE=lax (default) unless you have a specific same-site policy requirement.
The webhook endpoints use a separate Bearer token authentication.
Destructive action confirmation
Some destructive endpoints require an explicit confirmation header:
X-DD-Confirm-Action: <token>
If the header is missing or does not match the expected token, the API responds with 428 Precondition Required.
| Endpoint | Method | Required header |
|---|---|---|
/api/containers/:id/rollback | POST | X-DD-Confirm-Action: container-rollback |
/api/containers/:id | DELETE | X-DD-Confirm-Action: container-delete |
Endpoint overview
| Category | Endpoints | Description |
|---|---|---|
| App | GET /api/app, GET /api/server | Application info, server configuration, security runtime |
| Containers | GET /api/containers, POST, PATCH, DELETE | Container state, actions, update policy, security |
| Agents | GET /api/agents | Remote agent status and logs |
| Logs | GET /api/log | Application log entries |
| Registries | GET /api/registries | Registry configurations |
| Store | GET /api/store | Data store info |
| Triggers | GET /api/triggers | Trigger configurations |
| Watchers | GET /api/watchers | Watcher configurations |
Additional endpoints
| Endpoint | Method | Description |
|---|---|---|
/api/openapi.json | GET | OpenAPI 3.1.0 specification (machine-readable API docs) |
/health | GET | Health check (returns 200 when healthy) |
/metrics | GET | Prometheus metrics (optional auth via DD_SERVER_METRICS_AUTH) |
/api/events/ui | GET | Server-Sent Events for real-time UI updates |
/api/audit | GET | Audit trail (paginated, filterable by action/container/date) |
/api/notifications | GET, PATCH /:id | Notification rules (per-event enable/disable and trigger assignments) |
/api/settings | GET, PATCH | UI settings (internetless mode; PUT compatibility alias is deprecated) |
/api/server | GET | Server configuration and compatibility info (see App API) |
/api/server/security/runtime | GET | Security tools availability (see App API) |
/api/containers/groups | GET | Container groups (by stack/compose project) |
/api/webhook/watch | POST | Webhook — trigger watch on all containers |
/api/webhook/watch/:containerName | POST | Webhook — watch specific container |
/api/webhook/update/:containerName | POST | Webhook — update specific container |
Command endpoint convention
Some endpoints are intentional action commands and use POST under a resource path (for example container watch/scan/reveal). These are RPC-style operations rather than CRUD updates.
Notification rules
Notification rules control which events trigger notifications and which triggers receive them. Five default rules are created automatically.
Get all notification rules
curl http://drydock:3000/api/notifications
{
"data": [
{
"id": "update-available",
"name": "Update Available",
"description": "When a container has a new version",
"enabled": true,
"triggers": []
},
{
"id": "update-applied",
"name": "Update Applied",
"description": "After a container is successfully updated",
"enabled": true,
"triggers": []
},
{
"id": "update-failed",
"name": "Update Failed",
"description": "When an update fails or is rolled back",
"enabled": true,
"triggers": []
},
{
"id": "security-alert",
"name": "Security Alert",
"description": "Critical/High vulnerability detected",
"enabled": true,
"triggers": []
},
{
"id": "agent-disconnect",
"name": "Agent Disconnected",
"description": "When a remote agent loses connection",
"enabled": false,
"triggers": []
}
],
"total": 5
}Response fields
| Field | Type | Description |
|---|---|---|
data | array | List of notification rules |
total | integer | Number of rules returned |
data[].id | string | Rule identifier (e.g. update-available, security-alert) |
data[].name | string | Human-readable rule name |
data[].description | string | What this rule covers |
data[].enabled | boolean | Whether the rule is active |
data[].triggers | string[] | Trigger IDs that receive this notification (empty = all triggers) |
Update a notification rule
Update the enabled status and/or triggers list for a rule. Only the fields you include are changed.
curl -X PATCH http://drydock:3000/api/notifications/update-available \
-H 'Content-Type: application/json' \
-d '{"enabled": true, "triggers": ["slack.alerts", "ntfy.one"]}'Returns 200 with the updated rule, 400 if the body is invalid or a trigger ID is not recognized, or 404 if the rule ID does not exist.
Audit trail
Returns paginated audit log entries for container lifecycle events, updates, rollbacks, webhooks, and more.
Get audit entries
curl "http://drydock:3000/api/audit?offset=0&limit=25&action=update-applied&container=homeassistant"
{
"data": [
{
"id": "a1b2c3d4-...",
"timestamp": "2024-12-01T10:01:30.000Z",
"action": "update-applied",
"containerName": "homeassistant",
"containerImage": "homeassistant/home-assistant",
"fromVersion": "2024.11.3",
"toVersion": "2024.12.0",
"triggerName": "docker.local",
"status": "success",
"details": ""
}
],
"total": 42,
"limit": 25,
"offset": 0,
"hasMore": true
}Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
offset | integer | 0 | Number of entries to skip |
limit | integer | 50 | Results per page (max 200) |
action | string | Filter by action type | |
container | string | Filter by container name | |
from | string | ISO 8601 date -- only entries on or after this timestamp | |
to | string | ISO 8601 date -- only entries on or before this timestamp |
Action types
update-available, update-applied, update-failed, container-update, security-alert, agent-disconnect, container-added, container-removed, rollback, preview, container-start, container-stop, container-restart, webhook-watch, webhook-watch-container, webhook-update, hook-configured, hook-pre-success, hook-pre-failed, hook-post-success, hook-post-failed, auto-rollback, auth-login
Server-Sent Events (SSE)
The /api/events/ui endpoint provides real-time push notifications to the UI via Server-Sent Events.
Connect
curl -N -H "Accept: text/event-stream" http://drydock:3000/api/events/uiConnection limits: max 10 per IP, max 10 per session. Returns 429 when exceeded.
Event types
| Event | Payload | Description |
|---|---|---|
dd:connected | { "clientId": "...", "clientToken": "..." } | Sent immediately on connection. The clientToken is used for self-update acknowledgment. |
dd:heartbeat | {} | Sent every 15 seconds to keep the connection alive |
dd:container-added | Container object | A new container was discovered by a watcher |
dd:container-updated | Container object | An existing container's state changed (new tag, status, scan result) |
dd:container-removed | Container object | A container was removed |
dd:scan-started | { "containerId": "..." } | A security scan started for a container |
dd:scan-completed | { "containerId": "...", "status": "..." } | A security scan finished |
dd:self-update | { "opId": "...", "requiresAck": bool, "ackTimeoutMs": int, "startedAt": "..." } | Drydock is updating its own container. If requiresAck is true, the UI should POST an acknowledgment before the timeout. |
dd:agent-connected | Agent payload | A remote agent connected to the controller |
dd:agent-disconnected | Agent payload | A remote agent disconnected |
Self-update acknowledgment
When a dd:self-update event has requiresAck: true, the UI must POST an acknowledgment before the timeout expires. The clientId and clientToken are provided in the initial dd:connected event.
curl -X POST http://drydock:3000/api/events/ui/self-update/op-abc123/ack \
-H 'Content-Type: application/json' \
-d '{"clientId": "client-1", "clientToken": "tok-xyz"}'| Status | Response | Description |
|---|---|---|
| 202 | { "status": "accepted", "operationId": "..." } | Acknowledgment recorded |
| 202 | { "status": "ignored", "operationId": "..." } | No pending ack for this operation |
| 400 | { "error": "..." } | Missing required field (operationId, clientId, or clientToken) |
| 403 | { "status": "rejected", "operationId": "..." } | Invalid token, mismatched client, or client not bound to operation |
SSE message format
event: dd:container-updated
data: { "id": "31a61a...", "name": "homeassistant", ... }Settings
Persistent UI preferences stored in the drydock database.
Get settings
curl http://drydock:3000/api/settings
{
"internetlessMode": false
}Update settings
curl -X PATCH http://drydock:3000/api/settings \
-H 'Content-Type: application/json' \
-d '{"internetlessMode": true}'
{
"internetlessMode": true
}| Field | Type | Default | Description |
|---|---|---|---|
internetlessMode | boolean | false | When enabled, disables registry icon lookups and other external requests |
Returns 200 with the updated settings, or 400 if the body is invalid. PUT /api/settings is still accepted as a temporary compatibility alias but is deprecated.
Container groups
Returns containers grouped by stack or group label. Group priority: dd.group > wud.group > com.docker.compose.project > ungrouped.
curl http://drydock:3000/api/containers/groups
{
"data": [
{
"name": "monitoring",
"containers": [
{
"id": "31a61a...",
"name": "prometheus",
"displayName": "Prometheus",
"updateAvailable": false
},
{
"id": "e5f6a7...",
"name": "grafana",
"displayName": "Grafana",
"updateAvailable": true
}
],
"containerCount": 2,
"updatesAvailable": 1
},
{
"name": null,
"containers": [
{
"id": "abc123...",
"name": "standalone-app",
"displayName": "standalone-app",
"updateAvailable": false
}
],
"containerCount": 1,
"updatesAvailable": 0
}
],
"total": 2
}Response fields
| Field | Type | Description |
|---|---|---|
data | array | List of container groups |
total | integer | Number of groups returned |
data[].name | string | null | Group name (null for ungrouped containers) |
data[].containers | array | List of containers with id, name, displayName, updateAvailable |
data[].containerCount | integer | Number of containers in the group |
data[].updatesAvailable | integer | Number of containers with a pending update |
Authentication routes
When authentication is enabled, these endpoints manage the login flow. Auth routes are mounted at /auth (not /api/auth).
Get available strategies
Returns the list of configured authentication strategies. This endpoint is unauthenticated so the login screen can render.
curl http://drydock:3000/auth/strategiesA legacy alias is available at GET /api/auth/methods (rate-limited).
Login
Authenticate with credentials. The response sets a session cookie.
curl -X POST http://drydock:3000/auth/login \
-H 'Content-Type: application/json' \
-d '{"username": "admin", "password": "..."}'Get current user
Returns the currently authenticated user. Requires a valid session.
curl http://drydock:3000/auth/userLogout
End the current session.
curl -X POST http://drydock:3000/auth/logoutWhen the active auth strategy exposes an OIDC end-session URL, the response includes logoutUrl so the UI can complete IdP logout. If logoutUrl is missing, logout is local-session-only.
Remember me
Store a remember-me preference for the current authenticated session.
curl -X POST http://drydock:3000/auth/remember \
-H 'Content-Type: application/json' \
-d '{"remember": true}'OpenAPI
Machine-readable API documentation is available as an OpenAPI 3.1.0 specification.
curl http://drydock:3000/api/openapi.jsonThe spec covers all API endpoints with request/response schemas. You can import the URL into tools like Swagger UI, Redoc, or Postman for interactive exploration.