Reconcile
Reconciliation syncs the server's database with the actual state of running containers. It is the fix for situations where the sandbox count in the database is out of sync with what Docker reports - for example, after a host reboot, a crash, or a manual docker rm.
When reconciliation runs automatically
Section titled “When reconciliation runs automatically”The server reconciles on its own in three situations:
| Trigger | When |
|---|---|
| Startup | Once at boot, before accepting traffic, if AUTO_RECONCILE=true (the default). |
| Periodic loop | Every reconcile interval (default 30 s) while the server is running. |
| Docker events | Whenever a container exits or is destroyed, the event monitor updates the affected sandbox immediately. |
For most deployments automatic reconciliation is sufficient. The manual API is useful when you need the database to reflect reality right now - for example, immediately after clearing stuck containers so a new sandbox creation can succeed.
What reconciliation does
Section titled “What reconciliation does”For every sandbox row in the database, the server checks whether a matching Docker container exists:
- Container missing - the sandbox is marked
destroyed, its Caddy routes are removed, and any mounts are unmounted. - Container present - the sandbox's status, IP address, and Caddy routes are refreshed to match the live container state.
Rows that were already destroyed before the pass are left unchanged.
Trigger reconciliation manually
Section titled “Trigger reconciliation manually”await client.reconcile()client.reconcile()if err := client.Reconcile(ctx); err != nil { log.Fatal(err)}client.reconcile()?;client.reconcile();The call blocks until the full reconcile pass completes. On success it returns with no data. On failure it throws with the server-side error message.
Common scenario: capacity exceeded after a restart
Section titled “Common scenario: capacity exceeded after a restart”If the host was rebooted or containers were removed outside of the API, the database still counts those sandboxes against the CPU and memory budget. Creating a new sandbox fails with host capacity exceeded.
Fix:
// Force the DB to reflect what Docker actually seesawait client.reconcile()
// Now create succeedsconst sandbox = await client.create({ image: 'ubuntu:22.04' })client.reconcile()sandbox = client.create({'image': 'ubuntu:22.04'})if err := client.Reconcile(ctx); err != nil { log.Fatal(err)}sandbox, err := client.Create(ctx, sdktypes.CreateSandboxOptions{ Image: "ubuntu:22.04",})client.reconcile()?;let sandbox = client.create(CreateOptions { image: "ubuntu:22.04".to_string(), ..Default::default()})?;client.reconcile();Sandbox sandbox = client.create(new CreateOptions().setImage("ubuntu:22.04"));