Change the configuration through the API
Besides the file and Docker, the admin API is a provider of its own since v0.5: routes, services and middlewares written through it are merged into the configuration as name@api, validated and applied exactly like a file reload, stored in $RDOCK_DATA_DIR/state/, and restored after a restart. Objects from the file or Docker stay read-only through the API.
The examples run on the server, over the admin socket. From elsewhere, use http://127.0.0.1:2080 with -H "Authorization: Bearer $TOKEN" and a token with the operator role (see Security model).
api() { curl -s --unix-socket /var/lib/rdock/admin.sock -H 'Host: localhost' "$@"; }api http://localhost/api/v1/config # everything in use, with the owner of each objectapi -i http://localhost/api/v1/config/api # only the API's objects; note the ETag headerSecret values written in place are shown as ***, and basic-auth users entries as name:***; ${file:…} and ${env:…} references are shown as written since v0.5. Writing a document back with *** keeps the current secret, so you can read, edit and save without retyping secrets; a *** where there is no current value is refused with 422.
Change, always with If-Match
Section titled “Change, always with If-Match”Every change names the version it was based on, so two people cannot overwrite each other: without If-Match the answer is 428, with an older version 412 and the current ETag.
api -X PUT http://localhost/api/v1/config/api \ -H 'Content-Type: application/json' -H 'If-Match: "g7-3f2a9c1b0e4d5a6f"' \ -d '{"routes":{"shop":{"match":"Host(`shop.example.com`)","service":"shop"}}, "services":{"shop":{"backends":[{"address":"10.0.0.5:8080"}]}}}'PATCH changes part of it: Content-Type: application/merge-patch+json (RFC 7396, null removes) or application/json-patch+json (RFC 6902 operations). A configuration that would not be applied is refused with 422 and the diagnostics, and nothing changes: unlike Docker objects, a broken API object is never just left out.
Every write and dry run compiles the whole configuration, so at most 5 a second are accepted across all callers, in bursts of up to 10 since v0.5; beyond that the answer is 429 with Retry-After. Every caller (each API token, each user across their sessions, the CLI socket) may also make at most 20 requests of any kind a second, in bursts of up to 100, and the admin listener holds at most 256 connections. The admin side shares its CPU with the proxy, and these limits keep a runaway script from slowing the proxy down. Change many objects in one PUT or PATCH rather than one request each.
One object at a time
Section titled “One object at a time”GET /api/v1/routes, /services and /middlewares list every object with its owner; GET /api/v1/routes/{name} shows one. PUT /api/v1/routes/shop with the route as the body, or DELETE, changes one API object, with the same If-Match as above; objects owned by the file or Docker answer 403 with the provider to change them in.
Backends, certificates, providers and listeners
Section titled “Backends, certificates, providers and listeners”GET /api/v1/backendsshows every service’s backends: address, state (up,down,trial), requests in flight and whether they are drained.POST /api/v1/backends/drainwith{"service":"shop","backend":"10.0.0.5:8080"}stops new requests to it while open ones finish;/undrainputs it back. A drained backend is out even when the service fails open; a restart forgets drains.GET /api/v1/certificateslists configured and automatic certificates;POST /api/v1/certificates/shop.example.com/renewrenews the automatic certificate covering that name now (still within the back-off after failures).GET /api/v1/providersshows each provider’s state with the diagnostics about its objects;GET /api/v1/listenerslists listeners, which only a restart changes (ADR-0008).
Why did a request go there?
Section titled “Why did a request go there?”POST /api/v1/routes/explain with {"host": "shop.example.com", "path": "/cart", "method": "GET"} (optional listener, headers, client_ip) answers without sending anything: every route of the listener in evaluation order with matched, shadowed (it matches too, but an earlier route wins) or no match and the rule term that failed, then the winning route, its middlewares and its service’s backends with considered or the reason they are out. rdock routes explain --host shop.example.com --path /cart prints the same; the dashboard’s Routes page has Test a request.
The same from the command line
Section titled “The same from the command line”On the server the CLI uses the admin socket; elsewhere it needs --admin and RDOCK_ADMIN_TOKEN. The CLI speaks plain HTTP, so it sends the token only to a loopback address since v0.5; for a remote rdock, forward the admin port first (ssh -L 2080:127.0.0.1:2080 server) and use --admin 127.0.0.1:2080.
rdock config history # applied configurations with their idrdock config diff candidate.yaml # what a document would change, nothing appliedrdock config rollback 1793479942801rdock users list | add NAME --role operator | set NAME --disable | passwd NAME | reset-mfa NAME | remove NAMErdock tokens list | create NAME --role metrics --days 365 --allow 10.0.0.0/8 | revoke IDrdock backends list | drain SERVICE ADDRESS | undrain SERVICE ADDRESSrdock audit list --limit 50rdock routes explain --host shop.example.com --path /cart --header 'Authorization: Bearer x'Passwords for users add and users passwd are read from standard input.
Try first
Section titled “Try first”api -X POST http://localhost/api/v1/config/validate -H 'Content-Type: application/json' -d @candidate.jsonThe answer has valid, the diagnostics and a unified diff against the current API objects. Nothing is applied.
Checking a candidate needs the operator role, like changing one.
History and rollback
Section titled “History and rollback”GET /api/v1/config/history lists applied configurations, newest first, each with an id. POST /api/v1/config/rollback with {"id": …} and If-Match restores the API objects of that entry; people confirm their password first (POST /api/v1/auth/reauth). The file part is never rolled back by the API: the file is its own source of truth.
Export and import
Section titled “Export and import”GET /api/v1/config/export (admins, after a password check) returns {"version":1,"api":{…}}; POST /api/v1/config/import with that document and If-Match replaces the API objects on another instance. Write secrets as references: a document with masked *** values is refused.
GitOps
Section titled “GitOps”admin: api_writes: falseEvery configuration write through the API and the dashboard is then refused with 403; reading, history and dry runs keep working. Every write and rollback is in the audit log with the version hashes before and after.
OpenAPI and clients
Section titled “OpenAPI and clients”The whole admin API is described by an OpenAPI 3.1 document since v0.5, generated from the code, so it always matches the running version. Any authenticated caller can read it:
api http://localhost/api/openapi.json > openapi.jsonrdock schema --openapi > openapi.json # the same, without a running rdockA copy is in the repository as schema/openapi.json, together with a Go client in clients/go (generated with oapi-codegen) and the TypeScript types the dashboard uses (apps/dashboard/src/api). A test calls every operation in the document against a running rdock and checks each answer against it.