Understanding Coder Quiet Hours, Autostop & Activity Bumps

Last updated: June 25, 2026

Concepts

Coder has three distinct mechanisms that control when a workspace stops. They interact with each other and can produce surprising results if you don't understand how they're layered.

Mechanism What it does Scope
Autostop (TTL) Sets an initial deadline of build_completed_at + TTL Per-workspace or per-template
Activity Bump On detected activity (SSH, terminal, app, IDE), pushes the deadline forward by the template's activity_bump duration (default: 1 hour) Per-template
Quiet Hours + Autostop RequirementPremium Computes a hard max_deadline — the absolute latest the workspace can run — aligned to the user's quiet hours window Per-template + per-user

How Deadline and Max Deadline relate

  • Deadline: a soft stop time. Gets pushed forward by the activity bump when sessions are active. Governed by TTL + activity.
  • Max Deadline: a hard cap. The deadline can never be bumped past this. Governed by the template's autostop requirement + the user's quiet hours. If no autostop requirement is configured, max_deadline is zero (unbounded).

The autobuild system only looks at deadline. The core constraint is:

deadline <= max_deadline  (when max_deadline is non-zero)

autostop.go L99–108

1. Autostop (TTL)

When a workspace build completes, the deadline is set:

deadline = build_completed_at + TTL

The TTL comes from the workspace's custom value (if the template allows user overrides) or the template's default_ttl.

Autostart Interaction

If the calculated deadline would land after the workspace's next autostart time, Coder extends it to next_autostart + TTL. This prevents the workspace from stopping mid-morning when the user would expect it to keep running through the day.

autostop.go L133–151


2. Activity Bump

When Coder detects an active session (VS Code, JetBrains, terminal, SSH), it bumps the deadline forward. The bump amount is the template's activity_bump setting (default: 1 hour), not the original TTL.

Key Rules

From the SQL query source:

# Rule
1 New deadline = now() + activity_bump_duration
2 Deadline never decreases — uses GREATEST(current_deadline, now() + bump)
3 If max_deadline is set, deadline is capped: LEAST(bumped_deadline, max_deadline)
4 Bumps only fire after 95% of the bump interval has elapsed (e.g. ~57 min into a 1h bump) to reduce DB writes
5 Only applies when activity_bump > 0 on the template and the build has a non-zero deadline

The "72-hour autostop but it stopped at 63 hours" problem

⚠ Common Confusion This is the most frequent source of unexpected workspace shutdowns. The activity bump replaces the deadline with now + bump, not now + TTL.

Here's what happens with a 72h TTL and the default 1h activity bump:

Event Deadline
Build completes at T+0 T+72h
User is active; bump fires at ~T+57min now() + 1h ≈ T+1h57m — still before T+72h, so deadline stays at T+72h
Active through hour 62; last bump fires at ~T+62h Deadline = ~T+63h
User disconnects at T+62h No more bumps. Workspace stops at ~T+63h ❌

The workspace stopped 9 hours early because the activity bump replaces the deadline with now + 1h, not now + 72h.

Fix: Match the Activity Bump to Your Desired Inactivity Window

To get "stop 3 days after last activity":

Setting Value
Default autostop (TTL) 72 hours
Activity bump 72 hours
✅ Tip Now each bump pushes the deadline forward by 72h from the last detected activity. Once the user disconnects, the workspace stops 72 hours later — exactly as expected.

3. Quiet Hours Premium

Quiet hours define a per-user time window representing "after working hours." Workspaces subject to an autostop requirement will be force-stopped at the start of the user's quiet hours.

Deployment-Level Default

CODER_QUIET_HOURS_DEFAULT_SCHEDULE="CRON_TZ=America/Chicago 30 2 * * *"

Format: standard cron with CRON_TZ prefix. Only a single minute + hour value is supported (no ranges). Default is CRON_TZ=UTC 0 0 * * * (midnight UTC).

Allowing User Overrides

CODER_ALLOW_CUSTOM_QUIET_HOURS=true   # default: true

When set to false, all users use the deployment default regardless of what they've individually configured.

How Quiet Hours Are Used

⚠ Important Quiet hours only matter when the template has an autostop requirement configured (the DaysOfWeek bitmask is non-zero). If the autostop requirement days are unchecked / zero, quiet hours have no effect and the workspace lifecycle is governed purely by TTL + activity bump.

When an autostop requirement is active, Coder computes max_deadline:

  1. Take the user's quiet hours schedule in their timezone.
  2. Starting from the build completion time, find the next occurrence of the quiet hours cron that falls on a required day-of-week.
  3. That becomes the max_deadline.

autostop.go L157–241


4. The 2-Hour Leeway and the -15 Minute Buffer

autostopRequirementLeeway = 2 hours

If a workspace starts within 2 hours before the quiet hours time, Coder skips today's stop and uses the next eligible day. This prevents a workspace from being killed almost immediately after creation.

⚠ Watch OutExample: Quiet hours at 6:00 PM. User starts workspace at 4:30 PM (1.5h before quiet hours). Coder skips today's 6 PM stop — max_deadline becomes tomorrow's 6 PM (or the next eligible day). The workspace runs significantly longer than a user might expect.

autostop.go L20–24

autostopRequirementBuffer = -15 minutes

When checking whether today's quiet hours cron has passed, Coder subtracts 15 minutes. This prevents a race condition when autostart and quiet hours are set to the same clock time (e.g. both at midnight). Without this buffer, the workspace would always skip today and get an extra day of runtime.

autostop.go L26–41


5. Quiet Hours Changes Require a Workspace Restart

⚠ Common Confusion Updating a user's quiet hours schedule (or the deployment default) does not change max_deadline on workspaces that are already running. The new schedule only takes effect on the next build (i.e. the next stop/start cycle).

max_deadline (and deadline) are computed once, at build creation, by CalculateAutostop and persisted on the workspace build row. The user quiet hours PUT /api/v2/users/{user}/quiet-hours endpoint simply writes the new quiet_hours_schedule to the user row — it does not fan out to recalculate max_deadline for existing running builds.

This is different from changes to a template's autostop requirement: the enterprise template schedule store explicitly recalculates max_deadline and deadline for all running builds on that template when the template is updated.

When Will the New Schedule Apply?

  • On the next workspace start (manual stop+start, or the next autostart after the current build stops).
  • Until then, the currently-running build continues to enforce the max_deadline it was given when it last started.
⚠ Restart Timing Gotcha Because of the 2-hour leeway (section 4), restarting a workspace within 2 hours of the new quiet hours window will cause Coder to skip that window and pick the next eligible day. If you want the new schedule to apply tonight, restart well before the window opens.

enterprise/coderd/users.go enterprise/coderd/schedule/user.go template.go (recalc, for contrast)


6. Timezone Gotcha with Midnight

The default quiet hours is UTC midnight. If your users work in a different timezone, the "midnight" evaluation can land in the middle of a user's working day — a common reason workspaces appear not to stop as expected.

ℹ Note Quiet hours are evaluated in the schedule's own CRON_TZ, not in the timezone of the coderd host. The default is hardcoded to CRON_TZ=UTC regardless of where coderd is running, so the mismatch is between the schedule's timezone and where your users actually work.
✅ Recommendation Set CODER_QUIET_HOURS_DEFAULT_SCHEDULE to a time that makes sense for the majority of your users, and enable CODER_ALLOW_CUSTOM_QUIET_HOURS=true so users in other timezones can self-adjust. Remind users that they'll need to restart their workspace for a schedule change to take effect (see section 5).

Decision Flowchart

Workspace build completes │ ├─ TTL configured? │ ├─ Yes → deadline = build_completed_at + TTL │ └─ No → deadline = zero (no autostop) │ ├─ Template has autostop requirement (DaysOfWeek ≠ 0)? │ ├─ Yes → compute max_deadline from user quiet hours + required days │ │ ├─ Within 2h leeway? → skip to next eligible day │ │ └─ deadline = min(deadline, max_deadline) │ └─ No → max_deadline = zero (no hard cap) │ ├─ max_deadline is now FROZEN on this build until next stop/start. │ Changing user quiet hours later will NOT update it. │ └─ While running: └─ Activity detected? ├─ Yes → bump deadline = now + activity_bump (capped at max_deadline) │ (only fires after 95% of bump interval elapsed) └─ No → workspace stops when deadline passes

Quick Reference: Common Scenarios

Goal TTL Activity Bump Autostop Req Quiet Hours
Stop 8h after start, bump 1h on activity 8h 1h default Not needed N/A
Stop 3 days after last activity 72h 72h Not needed N/A
Force daily restart during off-hours Any Any Daily Set to user's off-hours
Force weekly restart on weekends Any Any Sat or Sun Set to user's off-hours
Pure TTL, no forced stops Any Any Days = none (0) Irrelevant
Change quiet hours and have it take effect Any Any Any Restart the workspace — change won't apply to the current build

External Tooling

If you need more direct control over workspace lifecycle — for example, bulk-stopping workspaces based on your own criteria rather than Coder's quiet hours logic — see coder-manipulate-ttl-and-workspaces.


Source References