Oracle Cloud Monitoring is the native observability layer for OCI: a metrics-first system that auto-collects performance and operational data from every OCI service, lets you query and aggregate them with a time-series SQL-like language (MQL), define alarms based on thresholds and state changes, and fan out notifications through Oracle Notifications Service (ONS) to email, PagerDuty, Slack, ServiceNow, and other endpoints. Unlike logging (which captures events and text) or APM (which traces request flows), Monitoring is built on metrics — the numerical measurements of system behavior over time — and is designed for scale: metrics arrive at 1-minute granularity by default, compress efficiently over time, and are queryable within seconds. This piece walks the whole stack: how metrics flow from OCI services, how to query and aggregate them with MQL, how to define and suppress alarms, how ONS fans out notifications, the cost model, and patterns for production-grade alerting.
Metrics: the currency of Monitoring
At the core of OCI Monitoring are metrics: timestamped numerical measurements of system state. Every OCI service — Compute (instances), Networking (VCNs, load balancers), Storage (Object Storage, Block Volumes), Databases (MySQL HeatWave, PostgreSQL), and dozens of others — emits metrics automatically into the Monitoring service. You do not install an agent, configure a daemon, or push events yourself; the metrics appear in your Monitoring namespace minutes after the resource is created.
Each metric has a namespace (a logical grouping, e.g., oci_compute, oci_network), a name (e.g., CpuUtilization, NetworksReceiveBytes), and one or more dimensions (labels that slice the metric, e.g., instanceId, compartmentId). A metric point is a tuple: (timestamp, value, namespace, metric name, dimensions). Monitoring stores metrics at 1-minute intervals by default, with a raw retention of 7 days, then auto-aggregates to hourly (90 days) and daily (1 year) for long-term trending. This design trades real-time detail for cost and queryability: you lose sub-minute granularity, but you can query a year of daily data in seconds without scanning terabytes.
Metric namespaces and dimensions: slicing the signal
Understanding namespaces and dimensions is key to writing useful queries. A namespace groups related metrics — oci_compute holds all Compute instance metrics, oci_network holds all networking metrics. Within a namespace, dimensions act like database indexes: they label each metric point so you can filter and aggregate by them. For Compute instances, the standard dimensions are instanceId (the OCID), compartmentId, availabilityDomain, and faultDomain. For load balancers, you get loadBalancerId, backendSetName, backendIp, and listenerPort.
Querying across dimensions is where MQL's power emerges. You can ask: 'What is the average CPU utilization, per compartment, for the last hour, for all instances in Availability Domain us-ashburn-1?' MQL lets you express that as a single query, and Monitoring fetches and aggregates the raw metric points in seconds. The alternative — pulling raw metrics and aggregating in application code — works for tens of metrics but breaks at hundreds or thousands.
Monitoring Query Language (MQL): SQL for time series
MQL is OCI Monitoring's query language: a SQL-like syntax for selecting, filtering, and aggregating metrics. A basic MQL query looks like:
MetricData{namespace="oci_compute", metric="CpuUtilization", statistic="Mean"}
.where(instanceId = '{your-instance-id}')
.within("5m")This reads as: 'Fetch the mean CPU utilization metric from the Compute namespace for a specific instance over the last 5 minutes.' The .where() clause is a filter; .within() sets the time window. MQL also supports grouping: .group_by(compartmentId) slices the results by compartment, so you see per-compartment averages instead of a single aggregate. And arithmetic: (MetricA + MetricB) / 2 computes derived metrics on the fly.
The key insight: MQL is evaluated at query time, so aggregations are cheap. You do not pre-compute all possible slices; you fetch raw 1-minute metric points and aggregate them server-side as needed. This means your queries adapt as your infrastructure grows, and old queries remain correct without manual maintenance.
Alarms: thresholds, state transitions, and suppression
A alarm is a rule that monitors an MQL query and triggers when the result crosses a threshold or changes state. Alarms are the bridge between passively watching metrics and actively notifying on-call teams. You define:
| Component | Meaning |
|---|---|
| Query | An MQL expression; the result is a number |
| Threshold | The value that triggers state change (e.g., CPU > 80%) |
| Trigger Delay | How long the threshold must be crossed before firing (e.g., 5 minutes) |
| Suppression | Optional rule to mute notifications for a window (e.g., during maintenance) |
| Notification Target | Where to send alerts (ONS topic, email, PagerDuty, etc.) |
Trigger Delay is crucial: it prevents flapping. A metric that spikes above 80% for 10 seconds and falls back is noise; a delay of 5 minutes means the threshold must stay breached for 5 continuous minutes before the alarm fires. This costs ~300 seconds of grace, but eliminates hundreds of false alarms. Similarly, when the metric falls below 80% again, the alarm transitions back to OK, and ONS sends a recovery message — allowing teams to know when an issue has actually resolved.
Suppression rules are often overlooked but critical in production. During a scheduled maintenance window, you know your metrics will look odd. Instead of disabling alerts (and forgetting to re-enable them), you create a suppression rule: 'From 2026-08-10 10:00 to 12:00 UTC, suppress all alarms in compartment X.' Metric data still flows, but ONS notifications are held back. The alarm itself records that it was suppressed, so you can audit the decision.
Oracle Notifications Service (ONS): the fanout engine
ONS is OCI's pub-sub system, and it is where alarms connect to the wider world. An alarm sends a message to an ONS topic, and that topic fans out to multiple subscriptions: email, HTTP webhooks, Slack, PagerDuty, ServiceNow, and more. You define the topic once, and the alarm publisher does not need to know about every destination.
A typical topology: alarm fires → ONS topic receives event → topic delivers to email, PagerDuty (for escalation), and a Slack webhook (for team visibility). If your on-call system is ServiceNow, you can wire an ITSM subscription so the alert automatically creates an incident. If you use a custom system, an HTTPS subscription delivers the full alert payload as a POST request.
One real-world gotcha: subscriptions require confirmation. When you add an email subscription, ONS sends a confirmation link to that email, and the subscription stays inactive until the link is clicked. For PagerDuty or ServiceNow integrations using API keys, no confirmation is needed. For email, remember to track which addresses are confirmed; unconfirmed subscriptions silently lose events.
Monitoring vs. Logging vs. APM: the observability split
OCI offers three observability layers, and conflating them is a source of confusion. Monitoring (this service) is metrics-first: aggregated, queryable, optimized for trend detection and alerting. Logging Service is event-first: raw text (application logs, audit logs, agent output), unstructured or semi-structured, optimized for search and forensics. APM Tracer is request-flow tracing: every request leaves a trace tree showing every service it touched and every millisecond spent; optimized for finding bottlenecks and debugging production bugs.
Choose your tool by the question: 'Am I watching a trend?' (Monitoring: CPU, memory, request count). 'Am I searching for a specific event?' (Logging: a failed login, an exception, a deploy event). 'Am I tracing one slow request?' (APM: why did this query take 5 seconds?). A production system uses all three. Monitoring fires the alarm ('database latency is high'); Logging gives details on errors that contributed; APM drills into the slow query itself.
The cost model: per-alarm pricing and free baselines
OCI Monitoring pricing is straightforward and friendly to low-volume users. You pay for:
| Item | Cost |
|---|---|
| Metrics ingestion and storage | Free (auto-collected from OCI services) |
| MQL queries (via REST API or Console) | Free for monitoring; paid for external integrations |
| Alarms | $0.10 per alarm per month (first 100 alarms free) |
| ONS notifications | Free for Monitoring alarms; $0.01 per 1K messages otherwise |
The free tier covers 100 alarms per month, which is generous for most teams starting out. Beyond that, $0.10 per alarm is negligible; even 1,000 alarms costs only $100/month. The key insight: you are not paying for the volume of metrics or the number of queries, only for the number of active alarm rules. This aligns cost with decision-making: every alarm represents a conscious choice to notify on a specific condition, so high alert volume is self-limiting by budget. The alternative model (per-metric or per-query pricing) encourages minimalism and misses real problems.
Building effective alarms: patterns and anti-patterns
Creating alarms is easy; creating alarms that fire when they should (and only when they should) is hard. A few patterns:
Per-resource alarms: One alarm per instance or database. This scales linearly with infrastructure and is tedious to maintain. Use this for mission-critical resources (your production database), not your dev cluster.
Per-compartment aggregates: One alarm per compartment, grouping all instances within it. 'If CPU > 70% on average across all prod instances, alert.' This catches fleet-wide trends without noise from individual spikes. Pair it with a dashboard showing per-instance detail so on-call can drill down.
Absence alarms: Alert when a metric stops flowing. If your application instance dies, it stops emitting heartbeat metrics. An absence alarm ('if no metric received in 10 minutes, alert') catches silent failures. MQL supports this with .has_data() predicates.
Anti-pattern — alarm spam: One alarm per threshold (CPU warning at 70%, critical at 90%, etc.), without grouping or deduplication. An overheating system fires dozens of redundant alerts, burying the real issue. Instead, use one alarm at your critical threshold (90%) with a long trigger delay (10 minutes for sustained load, not 1 minute for blips). If you need tiered alerts, use the alarm state (OK, Warning, Critical) and route them separately via ONS, not alarm count.
Integration patterns and automation
OCI Monitoring is a data source, not an action platform. To automate responses (e.g., scale up when CPU is high), you tie Monitoring to other services. Autoscaling: Create an instance pool autoscaling policy that watches a Monitoring metric and adjusts fleet size. This is the primary use case — Monitoring feeds data, Autoscaling makes decisions. Functions (serverless): Route ONS alarm messages to an OCI Function (Lambda equivalent), which can trigger deployments, run remediation scripts, or update firewall rules. Custom dashboards: Use the OCI Console or REST API to build live dashboards that refresh every minute, showing key metrics for your team. A well-designed dashboard is a team's operational heartbeat.
The REST API is the most flexible. A simple Python script can query current CPU across all instances, calculate percentiles, and post a summary to Slack every hour. OCI SDKs (Python, Java, Go, TypeScript) make this straightforward: authenticate with OCI credentials, call monitoring_client.summarize_metrics_data(), parse the result, and act.
Production readiness: SLA-grade alerting
If you are running production workloads on OCI, invest time in Monitoring. A few practices:
1. Alert on outcomes, not symptoms. Alert on 'requests failing' (bad), not 'CPU above 80%' (maybe bad, maybe fine). Use application-level metrics (error rate, latency percentiles) when possible, backed up by infrastructure metrics.
2. Establish SLIs and error budgets. Define a Service Level Indicator (e.g., 99.9% of requests complete in under 1 second). Monitoring should alert when you burn through your error budget faster than expected. This requires custom metrics from your application, but is worth the effort.
3. Test your alert paths. Once a quarter, trigger a test alarm and verify that email arrives, PagerDuty escalates, and Slack updates. Surprise: one subscription might be unconfirmed, or a team member left but their email is still subscribed.
4. Build a runbook for each alarm. When 'database latency > 5s' fires, what should on-call do? Check replication lag? Kill long-running queries? Restart the database? Document it, and link it in the alarm description. Monitoring supports HTML and markdown in the alarm message, so you can include a clickable link to your runbook.
The takeaway: metrics as a control plane
Monitoring is not just a dashboard tool; it is the data foundation for automatic decision-making. Autoscaling, failover, cost optimization, and security anomaly detection all depend on high-quality metrics. OCI makes this cheap (100 free alarms per month, $0.10 each after) and native (no agents to install, metrics auto-flow from every service). The challenge is not getting the data; it is defining the right thresholds and alert logic so your team wakes up for real issues, not noise.