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_deadlineis zero (unbounded).
The autobuild system only looks at deadline. The core constraint
is:
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.
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
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 |
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
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:
- Take the user's quiet hours schedule in their timezone.
- Starting from the build completion time, find the next occurrence of the quiet hours cron that falls on a required day-of-week.
-
That becomes the
max_deadline.
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.
max_deadline becomes tomorrow's 6 PM (or the next
eligible
day). The workspace runs significantly longer than a user might expect.
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.
5. Quiet Hours Changes Require a Workspace Restart
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_deadlineit was given when it last started.
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.
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.
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
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
-
📄
coderd/schedule/autostop.go— Deadline and max_deadline calculation -
📄
coderd/database/queries/activitybump.sql— Activity bump SQL logic -
📄
coderd/workspacestats/activitybump.go— Activity bump Go wrapper -
📄
enterprise/coderd/schedule/user.go— User quiet hours store (no recalculation on Set) -
📄
enterprise/coderd/schedule/template.go— Template schedule store (recalculates running builds, for contrast) - 🐛 coder/coder#14725 — "updating quiet hours does not change 'required to stop' time"
- 📄 Template scheduling docs — Official admin documentation