FlatRun collects its own metrics. Nothing to install, no separate time-series database to run, and no agent to configure on each container. The collector samples every container on the host, keeps the readings, and the same store feeds the charts, the alert engine, and anything you export to.
What is collected
Metric names follow the OpenTelemetry semantic conventions, so a name means the same thing here as it does in any other tool you already run.
| Metric | What it measures |
|---|---|
container.cpu.usage | Container CPU as a percentage of host capacity |
container.memory.usage and container.memory.limit | Container memory in bytes, against its limit |
container.network.io.rx and container.network.io.tx | Bytes in and out per second |
system.cpu.utilization | Host CPU |
system.memory.utilization, system.memory.usage, system.memory.limit | Host memory |
system.disk.utilization | Host disk |
Health and recovery
Alongside the numbers, FlatRun tracks each container's health as healthy, unhealthy, starting, or none for containers that declare no health check. When the watcher brings an unhealthy container back, it records a recovery event, so a container that has been quietly restarting all week is visible rather than merely still running.
Alert rules
A rule fires when a metric stays past a threshold for long enough. Each rule holds:
- Metric and scope: which reading to watch, for one deployment or across all of them.
- Comparison and threshold: above or below a value.
- For: how long the breach must persist before the rule fires.
- Targets: where the notification goes.
- Action: nothing, or restart the deployment.
The duration is what separates an alert from a twitch. A container sits at 100% CPU every time it starts, and a rule without a duration would page you for it.
A rule moves through ok, pending while the breach is building, and firing. When it fires, the event carries a snapshot of the biggest consumers at that moment, so "memory is high" arrives with the answer to "which container".
Rules that act
Give a rule the restart action and it repairs the deployment instead of only telling you about it. Two guardrails keep that from turning into a restart loop: only FlatRun-managed deployments are touched, and a deployment is not restarted again until its cooldown has passed.
Log rules
Metric rules watch numbers. Log rules watch what a deployment writes, and turn a fault it keeps hitting into the same kind of incident: notified through the same targets, listed beside the same events.
A rule holds:
- Scope: a deployment, optionally one service, and which log source to read.
- From level: error and worse by default. Below error a pattern is required, because a rule watching warnings with nothing else matches most of what a chatty application writes.
- Matching: an optional regular expression, applied to the parsed message rather than the raw line, so it cannot accidentally hit a timestamp or a container name.
- At least N within a window: three occurrences in five minutes by default. A single failure during a deploy is not an incident.
- Then stay quiet: one hour by default. Repeats inside that are counted onto the open incident instead of raising a new one.
Why the thresholds matter
A crashlooping container writes the same stack thousands of times an hour. FlatRun reduces each message to what stays the same across occurrences, ignoring the ids, timestamps, durations and addresses that vary, and treats everything with the same shape as one fault. Ten thousand copies of one crash raise one incident carrying a count of ten thousand, not ten thousand notifications.
Triage
A rule can ask the assistant to explain its incidents. It reads the failing line and a bounded window of the output around it, and answers with a summary, the likely cause, a suggested next step, and how confident it is. That lands in the notification, so the message says what broke rather than only that something did.
Four things keep it from becoming an expense:
- It is off for the app, and then off per rule. Two deliberate choices before anything reaches a model.
- Only an incident is ever explained, so every gate above runs first and for free.
- One explanation per distinct fault, reused while that fault keeps happening and cached for a week after.
- A daily ceiling in the agent (
ai.triage_daily_cap, 25 by default) and a hard cap on how much of the log a single explanation may read.
When the ceiling is reached, or the assistant is unreachable, the incident is still raised and still notified, marked as not explained. Alerting never depends on the model.
Secrets are stripped before anything is sent, using the same redaction the assistant uses elsewhere: the deployment's own environment values and the agent's credentials.
What happens when a rule fires
Notifying is one response. The mechanism behind it is a responder, and responders are registered rather than hard-coded, so the same incident can later be filed as an issue if one does not already exist, or handed to an agent that opens a pull request. Each incident carries a stable key for the fault, which is what lets a responder ask whether it has already acted on this one rather than filing a duplicate every time. Notify ships today.
Notification targets
Targets are configured once under settings and reused by every rule. A target is a shoutrrr URL, which covers webhooks, chat services, and email, and there is a test send so you find out the target works before an incident does.
Dashboards
Charts you assemble yourself, saved on the server rather than in one browser. Pick the metrics and the deployments that matter to you and keep that view; the panel you build is the one your colleague opens.
Sending metrics elsewhere
The built-in views are not a dead end. Set otlp_endpoint and the same readings are pushed to any OTLP
backend: an http or https URL speaks OTLP over HTTP, a bare host:port speaks gRPC. Leave it empty and
the standard OTEL_EXPORTER_OTLP_ENDPOINT environment variable is honoured instead. With neither set
nothing is pushed, and the metrics are still there to scrape from
/plugin/observability/metrics/prometheus in the Prometheus text exposition format.
Both paths read the store the UI draws from, so an external backend and FlatRun's own charts can never disagree about what a container did.
Settings
Kept flat, in .flatrun/observability.yml, and editable from the settings form the app contributes:
- sample_interval_seconds: how often containers are read. Default 5.
- retention_days: how far back history goes. Default 7. Samples older than the recent window are averaged into one point a minute, so a long retention stays cheap.
- auto_restart and restart_cooldown_seconds: whether unhealthy containers are restarted, and how long before the same deployment may be restarted again. Defaults on, and 120.
- otlp_endpoint: where metrics are pushed, if anywhere.
How it is delivered
Observability is a built-in app rather than something welded into the agent. It runs as a separate process that the
agent launches at startup and reaches through its plugin proxy under /plugin/observability, and it is
the same binary re-executed with a subcommand, so there is still one artifact to deploy and nothing extra to
install. It contributes its own UI: a Metrics & Health panel on each deployment, and its settings form.