Starlink metrics: offline vs online availability
Audience: Platform engineers and SREs evaluating on-premise Agent collection vs cloud telemetry API.
Source: Validated against a real rev4_catapult_proto1 dish (firmware 2025.12.04) running indoors with no satellite lock. Collection behavior below reflects the on-premise Agent check.
At a glance
| Layer | Count | What it means |
|---|---|---|
| Terminal interface | 200+ fields | Raw device surface SignalWeave analyzed to design the integration |
| On-premise Agent (shipped) | 67 metrics | Curated operational telemetry submitted to Datadog every 15 seconds |
| Held back | 140+ fields | Evaluated but not emitted—reserved for future use cases (metrics list) |
The on-premise Agent always collects all 67 metrics each cycle over the local LAN, regardless of satellite lock. Without a connection, many values reflect a degraded or “no link” state—but hardware alerts, alignment, ready states, and outage cause codes still populate. That is the core offline advantage over the cloud API.
All 67 metrics collected without satellite lock
The Agent polls the terminal every 15 seconds on the local network. All 67 curated metrics are emitted each cycle—no internet or satellite lock required for collection. Interpretation differs: some series carry full hardware state offline; others report sentinel values (-1, 0, 1.0 loss) until the link is up.
Meaningful offline — hardware & state
| Metric | Example Value | Why It Works Offline |
|---|---|---|
starlink.system.uptime_s | 185 | Local clock, counts since boot |
starlink.network.ethernet_speed_mbps | 1000 | LAN negotiation, no satellite needed |
starlink.alignment.tilt_angle_deg | 77.6° | IMU/accelerometer, local sensor |
starlink.alignment.attitude_uncertainty_deg | 51.8° | Local estimation |
starlink.alignment.boresight_azimuth_deg | 0.0° | Motor position |
starlink.alignment.boresight_elevation_deg | 0.0° | Motor position |
starlink.alignment.desired_azimuth_deg | 0.0° | Target from last schedule |
starlink.alignment.desired_elevation_deg | -0.0° | Target from last schedule |
starlink.alignment.azimuth_error_deg | 0.0° | Computed locally |
starlink.alignment.elevation_error_deg | 0.0° | Computed locally |
starlink.init.rf_ready_s | 25 | Boot timing, local measurement |
starlink.init.stable_connection_s | 0 | Boot timing |
starlink.init.gps_valid_s | 0 | Boot timing |
starlink.init.first_pop_ping_s | -1 | -1 means "not achieved yet" |
starlink.outage.cause | 1 | Local state (1 = no schedule) |
starlink.outage.duration_s | 0 | Local measurement |
starlink.outage.seconds_to_first_slot | -1 | -1 means "no slot available" |
starlink.system.software_update_progress | 0.0 | Local firmware state |
starlink.ready.scp | 1 | Subsystem self-test |
starlink.ready.l1l2 | 1 | Subsystem self-test |
starlink.ready.xphy | 1 | Subsystem self-test |
starlink.ready.aap | 1 | Subsystem self-test |
starlink.ready.rf | 1 | Subsystem self-test |
starlink.ready.cady | 0 | Needs satellite to become ready |
Meaningful offline — alert flags
| Metric | Description |
|---|---|
starlink.alerts.thermal_throttle | Dish overheating |
starlink.alerts.thermal_shutdown | Dish shut down from heat |
starlink.alerts.is_heating | Cold weather heater active |
starlink.alerts.motors_stuck | Motor hardware failure |
starlink.alerts.roaming | Outside registered area |
starlink.alerts.slow_ethernet_speeds | LAN link degraded |
starlink.alerts.install_pending | Software update waiting |
starlink.alerts.mast_not_near_vertical | Dish mount tilted |
starlink.alerts.dish_water_detected | Water on dish |
starlink.alerts.router_water_detected | Water on router |
starlink.alerts.no_ethernet_link | No cable connection |
starlink.alerts.unexpected_location | Geofence violation |
starlink.alerts.power_supply_thermal_throttle | PSU overheating |
starlink.alerts.slow_ethernet_speeds_100 | Ethernet at 100Mbps |
starlink.alerts.lower_signal_than_predicted | Signal degradation |
starlink.alerts.is_power_save_idle | Power save mode |
starlink.alerts.low_motor_current | Motor hardware issue |
starlink.alerts.obstruction_map_reset | Obstruction map cleared |
Collected offline — values reflect no satellite link
These metrics are collected but their values indicate the dish isn't connected:
| Metric | Offline Value | Meaning |
|---|---|---|
starlink.throughput.download_bps | 0.0 | No satellite = no download |
starlink.throughput.upload_bps | ~3000 | Tiny trickle (dish trying to phone home) |
starlink.latency.pop_ping_ms | -1.0 | -1 = no measurement possible |
starlink.packet_loss.pop_ping_drop_rate | 1.0 | 100% loss = no satellite |
starlink.signal.snr_above_noise_floor | 0 | No signal |
starlink.signal.snr_persistently_low | 0 | No signal to measure |
starlink.gps.satellite_count | 0 | No GPS fix |
starlink.gps.valid | 0 | GPS not locked |
starlink.obstruction.fraction_obstructed | 0.0 | Can't measure without satellite |
starlink.obstruction.currently_obstructed | 0 | Can't determine |
starlink.obstruction.time_obstructed_s | 0.0 | No measurement window |
starlink.obstruction.valid_duration_s | 0.0 | No valid measurement |
starlink.obstruction.patches_valid | 0 | No obstruction map |
starlink.power.dish_watts | 0.0 | UPSU not reporting (hardware-dependent) |
starlink.power.router_watts | 0.0 | UPSU not reporting (hardware-dependent) |
Metrics that require satellite lock for useful values
These are part of the 67-metric set but only produce operational values when the terminal has constellation contact:
| Metric | What It Needs |
|---|---|
starlink.throughput.download_bps (non-zero) | Active satellite link |
starlink.latency.pop_ping_ms (positive value) | Successful PoP ping |
starlink.packet_loss.pop_ping_drop_rate (< 1.0) | Some pings succeeding |
starlink.gps.satellite_count (> 0) | GPS antenna with sky view |
starlink.obstruction.fraction_obstructed (meaningful) | Satellite tracking to measure obstructions |
starlink.signal.snr_above_noise_floor (= 1) | Active RF link |
Cloud API vs On-Premise: What You Get When
| Scenario | Cloud API | On-premise Agent (67 metrics) |
|---|---|---|
| Ship online, good signal | ✅ ~16 metrics (summary) | ✅ 67 curated metrics |
| Ship online, weak signal / rain fade | ✅ Degraded values | ✅ Degraded values + alert flags |
| Ship in connectivity gap (international waters) | ❌ No data | ✅ All 67 metrics (hardware state, alerts, alignment) |
| Dish powered on but no satellite lock | ❌ No data | ✅ All 67 metrics (diagnose why link is down) |
| Dish powered off | ❌ No data | ❌ No data |
Key takeaway — maritime and remote deployments
The on-premise Agent delivers 67 curated metrics from 200+ analyzed terminal fields—not the raw device dump. During cloud API blind spots it still collects locally:
- Is it a thermal issue? (
alerts.thermal_throttle,alerts.thermal_shutdown) - Is it obstructed? (
alerts.mast_not_near_vertical, obstruction metrics) - Is it a hardware failure? (
alerts.motors_stuck,alerts.low_motor_current) - Is it a power issue? (
alerts.power_supply_thermal_throttle) - Is it water damage? (
alerts.dish_water_detected,alerts.router_water_detected) - Is it a network issue? (
alerts.no_ethernet_link,alerts.slow_ethernet_speeds) - Which subsystems are ready? (ready states show exactly what's working and what isn't)
This data is buffered locally by the Datadog Agent and uploaded to Datadog cloud as soon as connectivity returns — giving the shore-side ops team a complete picture of what happened during the outage.
Data Delivery: Real-Time Collection vs Buffered Upload
Important distinction for maritime/aviation deployments:
Collection (Agent → Dish): Always Real-Time
- The Agent collects from the dish every 15 seconds over the ship's LAN
- This works regardless of satellite/internet status — it's local network traffic only
- Data is immediately available to anything running locally on the ship
Upload (Agent → Datadog Cloud): Buffered When Offline
- The Agent tries to send metrics to
datadoghq.comover the internet every 15 seconds - If the satellite link is down, metrics queue in the Agent's local buffer
- When connectivity returns, the buffer flushes and Datadog receives all historical data with correct timestamps
- The dashboard timeline fills in retroactively — shore-side ops can see exactly what happened during the gap
Buffer Limits
- Default Agent buffer: ~4 hours of metric data
- Configurable via
forwarder_storage_max_size_in_bytesindatadog.yaml - For extended offline periods (days), increase the buffer or implement local persistent storage
What Shore-Side Ops Sees
| Ship Status | Datadog Dashboard |
|---|---|
| Online, good signal | Real-time data flowing, ~15s delay |
| Online, weak signal | Real-time but may see occasional gaps |
| Offline (connectivity gap) | Dashboard shows gap — no new data arriving |
| Back online after gap | Gap fills in retroactively with buffered data (correct timestamps) |
For Extended Offline Scenarios (days without connectivity)
If a customer needs guaranteed zero data loss during multi-day outages:
- Increase Agent buffer size (
forwarder_storage_max_size_in_bytes) - Or implement local persistent storage on the ship with a custom forwarder
- For typical maritime Starlink usage (intermittent gaps of minutes to hours), the default 4-hour buffer is sufficient
Bandwidth Impact: Metrics Payload Size
Concern: Cruise lines report that Starlink throttles when packets exceed 2MB during weak signal. Does the integration's outbound traffic risk hitting this limit?
Answer: No. The metrics payload is negligible.
Measured Payload Size (67 metrics with full maritime tags)
| Measurement | Size |
|---|---|
| Raw JSON payload (one check cycle, one dish) | 31.5 KB |
| Compressed payload (what the Agent actually sends) | 1.1 KB |
| Percentage of 2MB limit | 0.05% |
| Compression ratio | 97% |
| Per-metric average | 470 bytes raw, ~16 bytes compressed |
The Datadog Agent compresses all outbound payloads with gzip/zlib before transmission. Metric data compresses extremely well (~95% reduction) because of repeated tag strings and similar structure across metrics.
Scaled to Fleet Deployments
Values below assume the default 15 second min_collection_interval. Doubling the interval (e.g. 30s) halves daily/hourly volume; payload per cycle stays ~1.1 KB per dish.
| Fleet Size | Compressed per Cycle (15s) | Per Minute | Per Hour | Per Day |
|---|---|---|---|---|
| 1 dish | 1.1 KB | 4.4 KB | 264 KB | 6.3 MB |
| 10 dishes | 10.7 KB | 43 KB | 2.6 MB | 62 MB |
| 50 dishes | 53 KB | 213 KB | 12.8 MB | 307 MB |
| 100 dishes | 107 KB | 427 KB | 25.6 MB | 614 MB |
Context
- A single 1080p video stream uses ~5 Mbps = ~37 MB per minute
- Our integration at 50 dishes uses 220 KB per minute — 170x less than one video stream
- The 2MB packet size concern applies to individual HTTP requests, not cumulative bandwidth
- Each Agent payload is well under 100 KB compressed even at 50 dishes (the Agent batches intelligently)
Conclusion
The integration's bandwidth footprint is a rounding error on any maritime satellite link. Even during severe throttling (e.g. 1 Mbps available), the metrics payload would consume less than 0.2% of available bandwidth. This is not a concern for deployment.
How We Know It's Compressed (verified)
The Datadog Agent compresses all outbound payloads by default. This is confirmed in the Agent's own configuration file (datadog.yaml):
## @param use_compression - boolean - optional - default: true
## compresses logs before sending them.
# use_compression: true
## @param compression_level - integer - optional - default: 6
## The compression_level parameter accepts values from 0 (no compression)
## to 9 (maximum compression but higher resource usage).
# compression_level: 6- Compression is enabled by default — no configuration needed
- Default compression level is 6 (out of 9)
- This applies to both logs and metrics payloads
- The Agent sends
Content-Encoding: deflateheaders on all outbound HTTP requests - The 1.1 KB compressed figure is what actually traverses the satellite link — the raw 22.5 KB never leaves the machine uncompressed
Per-Metric Size Breakdown
All metrics are nearly the same size (287–325 bytes raw) because the payload structure is identical—only the metric name and value differ. Tags dominate byte count and are shared across all 67 metrics per terminal.
| Metric Count | Raw Size | Compressed Size | Savings |
|---|---|---|---|
| 5 metrics | 1.5 KB | 293 B | 80% |
| 10 metrics | 3.0 KB | 382 B | 87% |
| 20 metrics | 6.0 KB | 505 B | 92% |
| 30 metrics | 9.1 KB | 658 B | 93% |
| 67 metrics (full set) | ~31 KB | 1.1 KB | ~97% |
Key insight: Reducing metric count barely reduces bandwidth—the shared tags compress efficiently. Disabling whole metric groups via collect_* toggles is the right lever for constrained links, not trimming individual metrics from the 67-metric set. See the Configuration reference.
How Satellite Link Issues Affect Our Integration's Data Delivery
These are the operational constraints cruise lines face with Starlink. The question is: does our integration's outbound traffic (Agent sending metrics to Datadog cloud) get affected by these same issues?
Bottom line: Yes, our data travels over the same satellite link. But our payload is so small that these issues don't meaningfully affect us.
1. Satellite Handover (brief dropouts every few minutes)
What happens: Starlink satellites move fast. The dish switches from one satellite to another every few minutes, causing micro-second to millisecond connection blips.
Impact on our integration: None in practice. The Agent sends metrics over HTTPS (which runs on TCP). TCP automatically retransmits any packet that gets lost during a handover blip — the application layer never even sees it. A micro-second dropout is far too short to trigger a connection timeout. The Agent wouldn't know a handover happened.
Worst case: If a handover happens at the exact moment the Agent is mid-transmission, TCP retransmits the 1.1 KB payload in the next fraction of a second. No data loss, no user-visible impact.
2. Rain Fade (bandwidth drops sharply)
What happens: Heavy rain, snow, or dense clouds weaken the satellite signal. Available bandwidth drops significantly — sometimes from 200 Mbps down to single-digit Mbps.
Impact on our integration: Negligible. Our compressed payload is 1.1 KB per dish per cycle. Even if bandwidth drops to 100 Kbps (extremely degraded), transmitting 1.1 KB takes less than 0.1 seconds. The 2MB packet size concern the cruise lines mentioned is about their application traffic — video calls, passenger internet, operational systems. Our monitoring payload is 170x smaller than a single video stream.
Analogy: If the satellite link is a highway that narrows from 6 lanes to 1 lane during a storm, our metrics are a bicycle. The cars (video, passenger internet) get stuck in traffic. The bicycle still gets through fine.
3. Line-of-Sight Obstructions (signal blocked entirely)
What happens: Physical objects (ship structures, cranes, other vessels) temporarily block the dish's view of the sky. The connection drops completely for seconds to minutes.
Impact on our integration: The Agent can't send data during the blockage — same as everything else on the ship. But:
- The Agent buffers metrics locally during the obstruction
- When line-of-sight returns (seconds to minutes later), the buffer flushes automatically
- Datadog receives the data with correct timestamps — the timeline fills in retroactively
- No data is lost unless the obstruction lasts longer than the buffer window (~4 hours)
The irony: Our integration actually measures this exact problem. The obstruction.fraction_obstructed and pop_ping_drop_rate metrics show the customer exactly when and how often obstructions occur. So after the blockage clears, the ops team can see it in the dashboard.
Summary Table
| Satellite Issue | Duration | Our Payload Size | Impact on Our Integration |
|---|---|---|---|
| Handover dropout | Microseconds–milliseconds | 1.1 KB | None — TCP retransmits transparently |
| Rain fade (bandwidth drop) | Minutes–hours | 1.1 KB | None — payload is too small to be affected |
| Full obstruction (signal blocked) | Seconds–minutes | 1.1 KB | Buffered locally, delivered when signal returns |
| Extended outage (hours) | Hours | 1.1 KB × cycles | Buffered up to ~4 hours, then oldest data dropped |
What We Monitor vs What Affects Us
This is the important distinction for the customer:
These satellite issues affect the customer's operational traffic (passenger internet, video, VoIP, POS systems). That's their pain point — that's why they want monitoring.
These satellite issues do NOT meaningfully affect our monitoring traffic because our payload is 1.1 KB compressed. We're monitoring the problem without contributing to it.
Our integration tells the customer:
pop_ping_drop_rate→ shows handover gaps and signal loss in real-timeobstruction.fraction_obstructed→ quantifies how much sky view is blockedalerts.lower_signal_than_predicted→ indicates rain fade or atmospheric interferencethroughput.download_bps/upload_bps→ shows actual available bandwidthoutage.cause→ tells them WHY connectivity dropped
We don't add to the bandwidth problem. We measure it.
Verification: What These Metrics Actually Report During an Outage
Tested against a real dish with no satellite connection (indoors, no sky view). This simulates a total connectivity loss scenario — the worst case for a ship.
Reliable During Total Connectivity Loss ✅
These metrics correctly indicate "something is wrong" even when the dish has zero satellite contact:
| Metric | Value During Outage | Interpretation |
|---|---|---|
pop_ping_drop_rate | 1.0 (100%) | All pings failing — total loss confirmed |
throughput.download_bps | 0.0 | No download bandwidth — link is dead |
throughput.upload_bps | ~3000 (tiny trickle) | Dish is trying to connect but failing |
latency.pop_ping_ms | -1.0 | -1 means "unmeasurable" — no pings getting through |
outage.cause | 1 (no schedule) | Tells you WHY — dish can't find a satellite to connect to |
signal.snr_above_noise_floor | 0 | No usable signal detected |
These are the metrics that reliably answer "is the dish connected?" and "why not?" regardless of the failure mode.
Only Useful During PARTIAL Degradation (not total outage) ⚠️
These metrics require the dish to have some satellite contact to produce meaningful values:
| Metric | Value During Total Outage | Why It Doesn't Help Here |
|---|---|---|
obstruction.fraction_obstructed | 0.0 | Misleading — reports 0 because the dish can't measure obstructions without active satellite tracking. Does NOT mean "no obstructions." |
alerts.lower_signal_than_predicted | 0 (no alert) | Only fires when there IS a signal but it's weaker than expected. During total loss, there's no signal to compare against. |
When these DO work: During rain fade or partial obstruction — the dish still has satellite contact but the link is degraded. That's when obstruction.fraction_obstructed shows a real percentage and lower_signal_than_predicted fires. These are the "bandwidth is dropping" scenarios, not the "connection is dead" scenarios.
How to Interpret the Data for Each Scenario
| Scenario | What the Metrics Show |
|---|---|
| Satellite handover (brief dropout) | pop_ping_drop_rate spikes briefly then recovers. latency may spike. Throughput dips momentarily. |
| Rain fade (degraded bandwidth) | throughput drops gradually. lower_signal_than_predicted = 1. pop_ping_drop_rate increases but stays < 1.0. obstruction.fraction_obstructed may increase. |
| Physical obstruction (partial blockage) | obstruction.fraction_obstructed increases. pop_ping_drop_rate increases. Throughput drops. Periodic pattern if obstruction is from ship structure during turns. |
| Total loss (no satellite at all) | pop_ping_drop_rate = 1.0. latency = -1. throughput = 0. outage.cause gives the reason code. Alert metrics show 0 (not useful). |
Honest Limitations
We should be transparent with customers:
- The integration cannot distinguish between "no obstruction" and "can't measure obstruction" when the dish is fully offline
- The
lower_signal_than_predictedalert is a rain fade indicator only when the dish has partial connectivity - For total outage diagnosis, rely on
pop_ping_drop_rate,outage.cause, and the alert flags (thermal, motors, water, ethernet) rather than obstruction/signal metrics
Critical Clarification: Shore-Side Visibility During Total Outage
During a total satellite outage, the shore-side ops team cannot see anything in Datadog until connectivity returns.
Here's the timeline:
| Time | What's Happening on the Ship | What Shore-Side Sees in Datadog |
|---|---|---|
| T+0 | Satellite link drops | Last data point received — then nothing |
| T+1 min | Agent collecting locally, buffering | Gap in dashboard — no new data |
| T+30 min | Agent still collecting, buffer growing | Still a gap — they know something is wrong because data stopped |
| T+2 hours | Satellite link restored | Buffer flushes — gap fills in with all historical data (correct timestamps) |
| T+2 hours + 30s | Normal operation | Real-time data flowing again, plus full visibility into what happened during the outage |
What this means practically:
- Shore-side can detect an outage by the absence of data (metrics stop arriving)
- Shore-side cannot diagnose the outage in real-time — they have to wait for connectivity to return
- Shore-side gets full post-incident visibility once the buffer flushes — they can see exactly what happened, when, and why
- The Agent does NOT lose data during the outage (up to ~4 hours of buffer)
The only way to get real-time shore-side visibility during a total outage:
The ship would need a backup communication path that doesn't depend on Starlink — for example:
- A secondary VSAT link (many cruise ships have these as backup)
- Cellular connection (only works near shore)
- Iridium/Inmarsat low-bandwidth satellite (expensive, very low data rate)
This is a network architecture decision for the customer, not something our integration controls. If the ship has a backup link, the Agent will use it automatically (it just needs any internet path to reach datadoghq.com).
Setting expectations with customers:
Be upfront: "During a total Starlink outage, you'll see a gap in your dashboard. When connectivity returns, the gap fills in automatically with full detail. The Agent never stops collecting — you just can't see it in real-time until the data can reach the cloud."
This is not a limitation of our integration — it's a fundamental constraint of monitoring remote sites over the same link being monitored. Every monitoring solution has this same limitation unless there's a redundant communication path.
Why On-Premise Agent vs Cloud API (Both Are Blind During Outage)
Neither approach gives shore-side real-time visibility during a total outage. The difference is what happens after:
| Cloud API | On-Premise Agent | |
|---|---|---|
| During outage (shore-side) | No data | No data |
| After outage (shore-side) | Permanent gap — data is gone forever | Gap fills in with full 67-metric detail |
| Post-incident analysis | "We had a 2-hour outage. Unknown cause." | "Thermal throttle started at T+0, shutdown at T+3min, recovered at T+2h when ambient temp dropped." |
The value of on-premise is post-incident forensics, not real-time outage visibility.
For a cruise line ops team doing root cause analysis: the cloud API tells them that an outage happened. The on-premise Agent tells them why it happened and what the dish was doing the entire time it was offline.
