Instances
Instances are running copies of a service. They're the unit you logs, exec, and probe against.
An instance is a single running copy of a service. If you have scale: 3, you have 3 instances. They're created and destroyed by the reconciler — you never create one directly.
Identity
Each instance has:
- A stable ID like
api-instance-7c2e8a3b. - A reference back to its service and generation.
- A runner type (
dockerorprocess). - A state (
Pending,Starting,Running,Unhealthy,Stopping,Stopped,Failed). - The node it's running on (single-node today; multi-node tomorrow).
rune get instances
rune get instance api-instance-7c2e8a3b -o yaml
Lifecycle
Pending → Starting → Running ──┬──▶ Unhealthy → Stopping → Stopped
│
└──▶ Stopping → Stopped
│
└─▶ (replaced if scale unchanged)
The reconciler will replace failing instances automatically. Permanent failures (e.g., image pull errors that don't resolve) bubble up to the service status.
Operations on instances
| Action | Command |
|---|---|
| Logs | rune logs <instance-id> |
| Exec | rune exec <instance-id> |
| Health | rune health instance <instance-id> --checks |
| Inspect | rune get instance <instance-id> -o yaml |
| Kill | rune delete instance <instance-id> |
Killing an instance returns it to the reconciler, which replaces it. Don't use that as "scale up" — use rune scale.
Instance vs. service in commands
Most commands accept either a service name or an instance ID. Some, like exec, attach to a specific instance — if you pass a service name, Rune picks one for you.
rune exec api bash # picks any healthy instance of 'api'
rune exec api-instance-7c2e bash # specific instance
Per-instance state
Each instance carries:
Conditions[]— point-in-time check results (Ready, Healthy, ImagePulled, etc.).Restarts— how many times the runner has restarted it.LastFailureMessage— last error from the runner.StartedAt/FinishedAt.
Use rune get instance <id> -o yaml to read all of it. The most useful field for debugging is LastFailureMessage.
Postmortem: failed-instance retention
When an instance is restarted because of a health-check failure, Rune
preserves the failing container as a tombstone instead of removing it
straight away. The tombstone keeps the underlying docker container in its
stopped state so operators have time to inspect it — docker logs, the
container filesystem, the env it crashed with — instead of racing the
reconciler.
Tombstones:
- Appear in
rune get instances --show-failed(hidden by default). - Have
Status=Failedand a populatedFailedAt/FailureReason. - Are named
<original-name>-failed-<short-uuid>. - Keep working with
rune logs <tombstone-id>until they're reaped. - Don't count against the service's desired scale — the reconciler treats them as bookkeeping, not live instances.
# Find postmortem candidates for a flapping service
rune get instances -n prod --show-failed
# Inspect the dead container's logs
rune logs landing-0-failed-a3b9c1d8 -n prod
# Postmortem exec (the failing app does NOT re-run — server starts an
# inspection container with a sleep entrypoint instead).
rune exec --debug landing-0-failed-a3b9c1d8 -- ls /etc/secrets
Retention policy
A retention GC pass on the reconciler bounds how many tombstones survive
and for how long. Defaults are baked into the orchestrator for v1
(per-service cap of 3 tombstones; TTL of 1h, whichever fires first); a
failed_instance_retention block in the runefile lets operators override.
# runefile
failed_instance_retention:
per_service_cap: 5 # keep up to 5 most-recent tombstones per service
ttl: 2h # hard-evict anything older than 2h
snapshot_log_bytes: 200000 # reserved for v2 (post-eviction log snapshot)
When a tombstone is evicted, its container is removed and the instance record is marked Deleted; the existing deleted-instance retention sweep cleans the store row a few minutes later.
How --debug works under the hood
When you run rune exec --debug <tombstone-id> -- bash, the server:
- Looks up the tombstone (must be
Status=Failedwith a preservedContainerID— rejects Running instances with "drop the flag" and already-evicted tombstones with a "userune logs --previous" hint when that lands). - Builds a fresh docker container config from the tombstone's image, env, mounts, volumes, resources — same template the original used.
- Replaces the entrypoint with
sleep infinityso the failing app does not re-run when the new container starts. This is the whole reason a flag exists instead of justdocker starton the stopped container. - Names the sidecar
<tombstone-name>-debug-<short-uuid>and tags it with arune.debug=truelabel. - Pulls the image with
IfNotPresentpolicy (usually a no-op since the failing instance just ran it). - Starts the sidecar, opens an exec session against it for your command.
- Tears down the sidecar (stop + remove) when your session ends
— clean exit,
Ctrl+C, or transport disconnect all trigger cleanup via async.Once-guarded teardown wrapper on the exec stream.
The original failed container is never touched, so rune logs <tombstone-id> still works on it afterwards. Only the docker runner
supports --debug today; the process runner returns
FailedPrecondition because there's no image to clone.