Batch Operations
The /operations endpoint applies many changes in a single request instead of
one API call per change. It follows the JSON:API atomic operations
style, letting you add, update, or remove several domains, environment
variables, process scales, buildpacks, or HTTP basic-auth entries at once.
Batching also lets Dokku Pro coalesce the work. Changes to the same app and
resource are collapsed into a single dokku command where the CLI supports it
(for example, one config:set for many environment variables, or one ps:scale
for several process types), and each affected app is rebuilt at most once. So
importing ten environment variables runs one config:set and queues one
rebuild rather than ten of each.
Authentication is the same Bearer token used by the rest of the API; see the API overview for how to obtain one.
Request Format
POST to /@api/operations with either a linear:operations or a
parallel:operations array. Only one of the two may be present in a request.
Both are currently processed sequentially, in order.
Each operation object has:
op: one ofadd,update, orremove.data: the JSON:API primary data foraddandupdate. It carries atype, an optionalid, and anattributesobject with the same fields the equivalent single-resource endpoint accepts.ref: identifies the target of aremove(atypeand anid).
Supported Resources
| type | operations | attributes | rebuild |
|---|---|---|---|
envs |
add/update (set), remove | app_id, key, value |
yes |
domains |
add, update, remove | app_id, domain |
no |
ports |
add, update, remove | app_id, scheme, host_port, container_port |
no |
formations |
add/update (scale), remove (scale to 0) | app_id, process_type, quantity |
yes |
buildpacks |
add, remove | app_id, buildpack, position |
yes |
httpAuthIPs |
add, remove | app_id, ip_address |
no |
httpAuthUsers |
add/update, remove | app_id, username, password |
no |
add acts as an upsert for the resources whose dokku command is
create-or-overwrite (envs, formations, httpAuthUsers), so you do not need
to know whether a record already exists.
Example
Set several environment variables and add a domain in one request:
jwt_token="$(cat token.json | jq -r '.data.id')"
curl -H "Authorization: Bearer ${jwt_token}" \
-H "Content-Type: application/json" \
-d '{
"linear:operations": [
{ "op": "add", "data": { "type": "envs", "attributes": { "app_id": "node-js-app", "key": "PORT", "value": "5000" } } },
{ "op": "add", "data": { "type": "envs", "attributes": { "app_id": "node-js-app", "key": "DEBUG", "value": "true" } } },
{ "op": "add", "data": { "type": "domains", "attributes": { "app_id": "node-js-app", "domain": "node-js-app.example.com" } } }
]
}' \
"https://admin.dokku.me/@api/operations"
Responses
200: operations processed, no rebuild queued (for example a domains-only batch).202: operations processed and a rebuild was queued. TheContent-Locationheader points at the queued job (/@api/queueJobs/{id}), which you can poll for completion.400: the request could not be parsed, both operation lists were provided, or an operation was invalid. The body names the offending operation index.422: an operation failed while being applied. Earlier operations in the batch are not rolled back; the body reports the failing operation index and the results of the operations that did succeed.
Skipping the Rebuild
Send the Skip-Rebuild: true header to apply the changes without queuing a
rebuild, for example when you will deploy separately afterward. The response is
200 with no Content-Location.
Replacing an Entire Collection
To replace all of an app's domains at once, send an update targeting the
domains relationship with the full list as an array data:
{
"linear:operations": [
{
"op": "update",
"ref": { "type": "apps", "id": "node-js-app", "relationship": "domains" },
"data": [
{ "type": "domains", "attributes": { "domain": "node-js-app.example.com" } },
{ "type": "domains", "attributes": { "domain": "www.example.com" } }
]
}
]
}
The app's domains become exactly the provided list.
The same shape replaces all of an app's environment variables at once - target
the envs relationship with the full desired set as the array data:
{
"linear:operations": [
{
"op": "update",
"ref": { "type": "apps", "id": "node-js-app", "relationship": "envs" },
"data": [
{ "type": "envs", "attributes": { "key": "PORT", "value": "5000" } },
{ "type": "envs", "attributes": { "key": "DEBUG", "value": "true" } }
]
}
]
}
The app's config becomes exactly the provided set: keys in the payload are set
(only the new or changed ones are written), and any current variable not in the
payload is unset - including an empty data array, which clears all config.
Because there is no native "replace all config" command, Dokku Pro reconciles the
desired set against config:export and applies one config:set plus one
config:unset. You must therefore supply the complete desired set: any
dokku-managed variable you want to keep (for example DOKKU_* or GIT_REV) must
be included. Unlike a domains replacement, an envs replacement changes config and
so queues a rebuild, returning 202 with a Content-Location (a domains
replacement returns 200).
To replace all of an app's ports at once, send the same shape targeting the
ports relationship. Like a domains replacement it uses the atomic ports:set
(or ports:clear for an empty data array) and does not queue a rebuild,
returning 200:
{
"linear:operations": [
{
"op": "update",
"ref": { "type": "apps", "id": "node-js-app", "relationship": "ports" },
"data": [
{ "type": "ports", "attributes": { "scheme": "http", "host_port": 80, "container_port": 5000 } },
{ "type": "ports", "attributes": { "scheme": "https", "host_port": 443, "container_port": 5000 } }
]
}
]
}
To replace all of an app's process scale at once, target the formations
relationship. dokku has no "replace all scale" command, so Dokku Pro reads the
current scale and, in a single ps:scale --skip-deploy, sets the process types in
the payload and scales any current process type not in the payload to zero. You
must supply the complete desired scale; an empty data array scales every process
type to zero. Because a scale change only takes effect on the next rebuild, a
formations replacement queues a rebuild and returns 202 with a
Content-Location. Scaling a process type to zero leaves it listed at 0 rather
than removing it, so it still appears in a subsequent read:
{
"linear:operations": [
{
"op": "update",
"ref": { "type": "apps", "id": "node-js-app", "relationship": "formations" },
"data": [
{ "type": "formations", "attributes": { "process_type": "web", "quantity": 2 } },
{ "type": "formations", "attributes": { "process_type": "worker", "quantity": 1 } }
]
}
]
}
To replace all of an app's buildpacks at once, target the buildpacks
relationship with the full ordered list. dokku has no command that sets the whole
ordered list, so Dokku Pro runs buildpacks:clear and then re-adds each buildpack
in the order given - the array order is the buildpack order, and the position
attribute is ignored. An empty data array clears all buildpacks. Unlike a
domains replacement this is not atomic: a failure partway through leaves a
truncated list. A buildpack change takes effect on the next rebuild, so this
queues a rebuild and returns 202:
{
"linear:operations": [
{
"op": "update",
"ref": { "type": "apps", "id": "node-js-app", "relationship": "buildpacks" },
"data": [
{ "type": "buildpacks", "attributes": { "buildpack": "https://github.com/heroku/heroku-buildpack-nodejs.git" } },
{ "type": "buildpacks", "attributes": { "buildpack": "https://github.com/heroku/heroku-buildpack-ruby.git" } }
]
}
]
}
To replace all of an app's HTTP basic-auth allowed IPs at once, target the
httpAuthIPs relationship. Dokku Pro diffs the current allowed IPs against the
payload and applies http-auth:add-allowed-ip / http-auth:remove-allowed-ip to
reach the target set. An empty data array removes all allowed IPs. HTTP auth
changes apply without a rebuild, so this returns 200:
{
"linear:operations": [
{
"op": "update",
"ref": { "type": "apps", "id": "node-js-app", "relationship": "httpAuthIPs" },
"data": [
{ "type": "httpAuthIPs", "attributes": { "ip_address": "1.2.3.4" } },
{ "type": "httpAuthIPs", "attributes": { "ip_address": "5.6.7.8" } }
]
}
]
}
To replace all of an app's HTTP basic-auth users at once, target the
httpAuthUsers relationship. Each user must include its password, since
passwords are write-only and cannot be read back to compare. Dokku Pro (re-)adds
every user in the payload with http-auth:add-user (which overwrites an existing
user's password) and removes any current user not in the payload. An empty data
array removes all users. This returns 200 with no rebuild:
{
"linear:operations": [
{
"op": "update",
"ref": { "type": "apps", "id": "node-js-app", "relationship": "httpAuthUsers" },
"data": [
{ "type": "httpAuthUsers", "attributes": { "username": "admin", "password": "s3cret" } },
{ "type": "httpAuthUsers", "attributes": { "username": "ops", "password": "hunter2" } }
]
}
]
}
Collection replacement is supported for domains, ports, envs, formations,
buildpacks, httpAuthIPs, and httpAuthUsers.