hashicorp/nomad · error
self-drain exceeded deadline
Error message
self-drain exceeded deadline
What it means
DrainSelf drains the local node as part of nomad agent leave. It monitors drain progress; when the RPC error is context.DeadlineExceeded or context.Canceled, the self-drain did not finish within its deadline and this error is returned to Leave. The node may be left in a partially drained state.
Source
Thrown at client/drain.go:74
if drainSpec.Deadline > 0 {
// if we set this context to the deadline, the server will reach the
// deadline but not get a chance to record it before this context
// expires, resulting in spurious errors. So extend the deadline here by
// a few seconds
ctx, cancel = context.WithTimeout(context.Background(), drainSpec.Deadline+(5*time.Second))
defer cancel()
}
statusCheckInterval := time.Second
logger.Info("monitoring self-drain")
err = c.pollServerForDrainStatus(ctx, statusCheckInterval)
switch err {
case nil:
logger.Debug("self-drain complete")
return nil
case context.DeadlineExceeded, context.Canceled:
logger.Error("self-drain exceeded deadline")
return fmt.Errorf("self-drain exceeded deadline")
default:
logger.Error("could not check node status, falling back to local status checks", "error", err)
}
err = c.pollLocalStatusForDrainStatus(ctx, statusCheckInterval, drainSpec)
if err != nil {
return fmt.Errorf("self-drain exceeded deadline")
}
logger.Debug("self-drain complete")
return nil
}
// pollServerForDrainStatus will poll the server periodically for the client's
// drain status, returning an error if the context expires or get any error from
// the RPC call. If this function returns nil, the drain was successful.
func (c *Client) pollServerForDrainStatus(ctx context.Context, interval time.Duration) error {
timer, stop := helper.NewSafeTimer(0)View on GitHub (pinned to 482b49bf1a)
Solutions
- Increase the drain deadline (nomad node drain -deadline) or the agent leave deadline and retry.
- Check nomad job status for allocations that failed to migrate — fix scheduling constraints so replacements can be placed.
- Mark other nodes eligible (nomad node eligibility -enable) so draining allocs have somewhere to go.
- Retry the drain manually with nomad node drain -enable on the node once blockers are fixed.
Example fix
// before: short deadline on busy node c.drainingCtx, cancel = context.WithTimeout(ctx, 1*time.Minute) // after: allow more time ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute)
Defensive patterns
Strategy: retry
Validate before calling
// ensure capacity exists before draining
nodes, _ := client.Nodes().List(nil)
if eligibleHealthy(nodes) < 1 { return errors.New("no eligible nodes to receive drained allocs") } Try / catch
if err := c.DrainSelf(deadline); err != nil && err.Error() == "self-drain exceeded deadline" {
// raise deadline, fix stuck allocs, retry drain
log.Warnf("self-drain deadline hit: %v", err)
} Prevention
- Set a generous drain deadline appropriate for node size
- Mark other nodes eligible so migrations can place
- Check for constrained jobs that can only run on the draining node
When it happens
Trigger: The drain deadline (drainSpec.Deadline or the default leave deadline) elapses before all allocations on the node are migrated, or the monitoring context is canceled while allocations remain.
Common situations: Slow/stuck allocations that cannot be rescheduled (no eligible clients for their class, disconnected clients); large node with many allocs and a short leave deadline; scheduler backlog.
Related errors
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/8e428a3080f9ee5f.
Report an issue: GitHub.