Skip to content

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 of add, update, or remove.
  • data: the JSON:API primary data for add and update. It carries a type, an optional id, and an attributes object with the same fields the equivalent single-resource endpoint accepts.
  • ref: identifies the target of a remove (a type and an id).

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. The Content-Location header 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.