The saving
Running weekdays 07:00–19:00 instead of 24×7 is 60 of 168 hours: roughly 60% less compute cost for those resources. Storage keeps billing.
1. Tags first
| Defined tag | Example |
|---|---|
Operations.Environment | dev, test, prod (never schedule prod) |
Operations.Schedule | office-hours-ist |
Operations.ScheduleOverrideUntil | 2026-10-31 |
Use tag defaults per compartment so new resources get the right environment tag.
2. Resource Scheduler
Resource Scheduler creates schedules that run an action (start or stop) on selected resources at a recurrence (cron-style or iCal). Resources can be selected statically or dynamically by tags/compartment, so new dev resources are included automatically. Check the list of supported resource types (compute instances, instance pools, Autonomous Database and others are supported; the list grows).
Typical pair of schedules:
| Schedule | Action | Recurrence (IST) |
|---|---|---|
dev-stop-evening | STOP | Mon–Fri 19:15 |
dev-start-morning | START | Mon–Fri 06:45 |
IAM: Resource Scheduler needs policies allowing it to manage the selected resources. Example shape (adjust to the documented resource principal syntax):
Allow any-user to manage instance-family in compartment dev where all {request.principal.type = 'resourceschedule'}
Allow any-user to manage autonomous-database-family in compartment dev where all {request.principal.type = 'resourceschedule'}
3. Resources it doesn't cover: Functions
For anything not supported, use a scheduled Function (via Resource Scheduler invoking a function, or another scheduler) that reads tags and calls the API:
import oci, datetime
def stop_dev_instances(signer, compartment_id):
compute = oci.core.ComputeClient(config={}, signer=signer)
today = datetime.date.today().isoformat()
for inst in oci.pagination.list_call_get_all_results(compute.list_instances, compartment_id,
lifecycle_state="RUNNING").data:
ops = (inst.defined_tags or {}).get("Operations", {})
if ops.get("Environment") not in ("dev", "test"):
continue
if ops.get("ScheduleOverrideUntil", "") >= today:
continue
compute.instance_action(inst.id, "SOFTSTOP")
Use the function's resource principal (oci.auth.signers.get_resource_principals_signer()) and a dynamic group with least-privilege policies.
4. Per-resource notes
| Resource | Stop action | Notes |
|---|---|---|
| Compute instance | SOFTSTOP (graceful) | Boot/block volumes still billed |
| Instance pool | Stop pool | Or scale to 0 |
| Autonomous Database | Stop | Storage billed; reconnect after start |
| Base Database node | oci db node stop | Check behaviour for RAC |
| OKE node pool | Scale node pool size to 0 | Control plane / cluster fee continues on enhanced clusters |
| Load balancer | No stop | Consider deleting dev LBs in ephemeral environments |
5. Overrides and notifications
- Override tag with a maximum length (e.g. 14 days), checked by the function/automation.
- A message to the team channel 30 minutes before stop (Notifications topic → chat).
- Avoid stopping during running CI/CD deployments.
6. Side effects
- Scheduled jobs inside dev VMs and databases: move them into working hours.
- Ephemeral public IPs change after stop/start. Use reserved IPs or DNS.
- Shared dev services used by other teams may need a longer window.
7. Report savings
Monthly Cost Analysis for dev/test compartments, before and after, shared with the teams.
Tip
Pilot with one compartment for two weeks. Someone will always find a server that "must" stay on; the override tag handles it without weakening the schedule for everyone else.
Checklist
- Environment and schedule tags with defaults
- Resource Scheduler stop/start schedules with dynamic selection
- IAM policies for the scheduler and functions
- Functions for unsupported resources
- Override tag and pre-stop notification
- Side effects checked; savings reported