ProxySQL deployments tend to land at one of two monitoring extremes: a process check and a port check, or a full dashboard nobody acts on. The gap between “is it running” and “is it healthy” is where most incidents live. This article maps four monitoring maturity levels, each adding signals that catch failure modes the previous level cannot.
Use this as a self-assessment. Find the highest level where you have every signal covered, then look at what the next level adds. All signals come from ProxySQL’s own stats_* tables and host-level metrics, queryable through the admin interface on port 6032.
Scope note: this model covers ProxySQL as a MySQL-wire-protocol proxy. ProxySQL does not support PostgreSQL.
flowchart TD
L1["Level 1: Survival
Is ProxySQL up?"]
L2["Level 2: Operational
Is traffic flowing correctly?"]
L3["Level 3: Mature
Is the proxy itself healthy?"]
L4["Level 4: Expert
Is the proxy optimal?"]
L1 --> L2
L2 --> L3
L3 --> L4Level 1: survival
The question: is ProxySQL alive and able to serve traffic? Without these signals, you rely on users to report outages.
| Signal | Source | What it tells you |
|---|---|---|
| Process running | Host-level process check | ProxySQL has not crashed or been OOM-killed |
| Port 6033 accepting connections | External TCP connect check | The listener is not hung |
| At least 1 ONLINE backend per hostgroup | stats_mysql_connection_pool status column | Queries have a destination |
| ConnERR per backend | stats_mysql_connection_pool | ProxySQL cannot reach a backend |
| Client_Connections_aborted | stats_mysql_global | Clients are being rejected |
| Process RSS | /proc/<pid>/status or stats_memory_metrics | OOM risk |
| ProxySQL_Uptime | stats_mysql_global | Cold-start gating for other alerts |
Key points:
- Port 6033 is an external check, not a stats metric. ProxySQL does not expose listener health in its stats tables. You need a TCP connect check or a synthetic MySQL protocol check from outside the process. If the port stops accepting connections, page unconditionally. The proxy is down for all clients.
- ConnERR is cumulative. Compute a rate (delta over time) for it to be meaningful. A raw counter of 5000 tells you nothing without knowing how fast it is growing.
- Zero ONLINE backends is the only true PAGE condition at this level, but needs conditions to avoid false positives: sustained for more than 60 seconds,
ProxySQL_Uptime > 600to exclude cold start, andClient_Connections_non_idle > 0orQuestionsrate above zero to exclude idle instances. - SHUNNED is not a page. SHUNNED is ProxySQL’s protection mechanism. A backend in SHUNNED state often self-corrects when replication lag subsides or connection errors stop. Page only when zero backends remain ONLINE in a hostgroup.
Level 2: operational
The question: is traffic flowing through the proxy correctly, and are resources under control? This is the minimum level for production.
| Signal | Source | What it tells you |
|---|---|---|
| ConnUsed / ConnFree per backend | stats_mysql_connection_pool | Backend pool utilization and pressure |
| Client connection rates (created, aborted) | stats_mysql_global | Frontend connection patterns |
| Server connection rates (created, aborted, delayed) | stats_mysql_global | Backend connection churn and queueing |
| Questions rate | stats_mysql_global | Overall throughput baseline |
| Slow_queries rate | stats_mysql_global | Queries exceeding mysql-long_query_time |
| Monitor check results (OK/ERR per type) | stats_mysql_global | Backend health probe success rates |
| Per-user connection utilization | stats_mysql_users | Users approaching frontend_max_connections |
| Access_Denied counters | stats_mysql_global | Auth failures broken down by cause |
| ProxySQL process CPU | Host-level | Worker thread saturation |
| jemalloc_resident | stats_memory_metrics | Physical memory footprint |
Key points:
- ConnFree == 0 does not mean saturation. ProxySQL can still create new connections up to the backend’s
max_connectionsconfigured inmysql_servers. True saturation means ProxySQL cannot create new connections AND has no free ones. The symptom is ConnERR increasing because the backend rejected the connection attempt. - Server_Connections_delayed above zero is direct evidence of pool pressure. Queries had to wait for a free backend connection. More actionable than ConnFree alone.
- Monitor check results are a leading indicator. Sustained failure rate increases across check types (connect, ping, read_only, replication_lag) precede backend status changes. Watch
MySQL_Monitor_Workers == 0specifically: it means monitoring is disabled and all status decisions are stale. - Access_Denied has three distinct counters.
Access_Denied_Wrong_Passwordindicates credential mismatch or brute force.Access_Denied_Max_Connectionsmeans global connection limits are hit.Access_Denied_Max_User_Connectionsmeans per-user limits are hit. Different causes, different responses. - Use ratios, not absolute thresholds. Setting “ConnUsed > 100” fails across environments. A dev instance with max_connections=50 and production with max_connections=1000 need different thresholds. Track
ConnUsed / max_connectionsas a percentage. - Slow_queries can spike during batch operations and schema migrations. It is a TICKET signal, not a PAGE. The actual service impact is better measured by whether
Questionsrate drops.
Level 3: mature
The question: is the proxy itself healthy, not just passing traffic? This level catches failure modes that look fine at Level 2 until they cascade.
| Signal | Source | What it tells you |
|---|---|---|
| Query cache hit ratio | stats_mysql_global (Query_Cache_get_ok / (get_ok + get_err)) | Cache effectiveness |
| Query_Cache_Purged rate | stats_mysql_global | LRU eviction pressure |
| Per-command histograms | stats_mysql_commands_counters | Latency distribution per SQL command |
| Per-backend latency | stats_mysql_connection_pool Latency_us | Monitor-measured ping latency per backend |
| Active_Transactions | stats_mysql_global | Connections pinning backend connections |
| Client_Connections_hostgroup_locked | stats_mysql_global | Multiplexing broken for these sessions |
| Memory breakdown | stats_memory_metrics | Per-subsystem memory (Auth, query_digest, jemalloc) |
| ConnPool_get_conn_failure | stats_mysql_global | Most direct indicator of pool starvation |
| backend_lagging/offline_during_query | stats_mysql_global | Queries that hit an unavailable backend mid-flight |
Key points:
- ConnPool_get_conn_failure is the signal most teams miss. ConnUsed and ConnFree tell you about pool state. ConnPool_get_conn_failure tells you ProxySQL actually tried to get a connection and failed. More actionable than watching ConnFree approach zero.
- Latency_us is ping latency, not query latency. The
Latency_uscolumn instats_mysql_connection_poolcomes from the monitor module’s ping check. It represents network round-trip time, not query execution time. For real query latency distribution, use the histogram buckets instats_mysql_commands_counters(cnt_100usthroughcnt_INFs). - backend_lagging_during_query and backend_offline_during_query count actual failures. These are not theoretical risks. They count queries that were already in flight when the backend became unavailable or was detected as lagging. The query may have returned stale data or failed entirely.
- Query_Cache_Purged rate distinguishes healthy saturation from harmful churn. A cache at capacity with a stable hit ratio is normal LRU behavior. If purge rate is high and hit ratio is declining simultaneously, the cache is too small for the working set and is churning with no benefit.
- Querying
stats_memory_metricshas a performance cost. It can cause latency spikes and CPU load. ProxySQL’s Prometheus exporter uses a separate, longer collection interval for memory metrics for this reason. Do not scrape this table at high frequency. - query_digest_memory grows with unique query patterns and is not bounded. The
stats_mysql_query_digesttable accumulates entries in memory. The only ways to reclaim this memory are restarting ProxySQL or queryingstats_mysql_query_digest_reset. Warning:stats_mysql_query_digest_resetreads and then clears all digest statistics. Use it only when you can afford to lose historical digest data. - Active_Transactions pin backend connections. A high count relative to total client connections means multiplexing is degraded. Each active transaction holds a dedicated backend connection that cannot be reused.
Level 4: expert
The question: is the proxy operating optimally, and is its configuration correct? These are the signals you add after your third ProxySQL incident.
| Signal | Source | What it tells you |
|---|---|---|
| Multiplexing ratio | Computed from stats_mysql_global | Frontend-to-backend connection ratio |
| Pinning ratio | Computed from stats_mysql_global | Fraction of sessions with broken multiplexing |
| Per-query-rule stats | stats_mysql_query_rules hits column | Rule effectiveness and routing correctness |
| Query digest top-N | stats_mysql_query_digest | Workload profiling and anomaly detection |
| Cluster checksums | stats_proxysql_servers_checksums | Config divergence between ProxySQL nodes |
| Monitor freshness | monitor.mysql_server_*_log timestamps | Gap between configured and actual check intervals |
| Config drift | Comparing mysql_servers vs runtime_mysql_servers | Unsaved or unloaded changes |
| Query_Processor_time_nsec | stats_mysql_global | CPU spent in the query processor |
| Per-thread CPU | ps -L -o tid,pcpu -p $(pidof proxysql) | Hot-thread detection |
| stats_mysql_errors per-errno | stats_mysql_errors | Granular error breakdown by MySQL error code |
Key points:
- Multiplexing ratio = Client_Connections_connected / Server_Connections_connected. A healthy proxy should have this ratio well above 1, meaning many clients share few backend connections. When it approaches 1:1, ProxySQL provides no pooling benefit while adding latency overhead. The pinning ratio (
Client_Connections_hostgroup_locked / Client_Connections_connected) tells you what fraction of sessions have broken multiplexing. A pinning ratio above 50% sustained is serious degradation. - stats_mysql_query_rules.hits resets on rule load. Every time you run
LOAD MYSQL QUERY RULES TO RUNTIME, the hits counter resets to zero. Historical trend data is lost silently. If a critical write-routing rule shows zero hits after a reload, routing may be broken. - Cluster checksum divergence is latent risk, not immediate outage. Nodes continue operating with their own config. But if checksums disagree for
mysql_serversormysql_query_rules, traffic may be routed differently depending on which ProxySQL instance a client connects to. This only applies to ProxySQL Cluster deployments usingproxysql_servers. - Config drift is invisible to metrics. There is no stat that says “your runtime config differs from disk.” You detect it by comparing tables:
mysql_servers(MEMORY layer) vsruntime_mysql_servers(RUNTIME layer). If they differ, someone made a change that was not loaded to runtime, or loaded a change that was not saved to disk. A restart will revert to disk state. - Per-thread CPU reveals hot-spotting. ProxySQL distributes connections across worker threads (
mysql-threads, default 4). If one thread is pinned at 100% CPU while others are idle, you have a load distribution problem. Thread count is a hard ceiling on parallelism and cannot be changed without restart. - stats_mysql_errors gives the most granular error information. It breaks down errors by MySQL error number, hostgroup, user, and schema. Error 1040 (too many connections) from a backend is a different problem from error 1045 (access denied) or error 9001+ (ProxySQL internal errors). ProxySQL-generated errors in the 9000+ range always warrant investigation.
How Netdata helps
- Per-second collection from ProxySQL’s admin interface. Netdata queries the
stats_*tables at high frequency, giving rate-based views of cumulative counters like ConnERR, Questions, and ConnPool_get_conn_failure without manual delta calculation. - Correlation across levels. A multiplexing ratio drop (Level 4) that correlates with a ConnUsed spike (Level 2) and an Active_Transactions increase (Level 3) points to transaction-driven degradation. Viewing these signals on a single timeline shortens diagnosis from “which metric changed” to “why did it change together.”
- Backend health and monitor checks on the same dashboard. Netdata surfaces per-backend status, ConnERR rates, and monitor check OK/ERR ratios together, making it obvious whether a SHUNNED backend is caused by network issues, replication lag, or credential problems.
- Host-level signals alongside ProxySQL metrics. Process RSS, per-thread CPU, and file descriptor counts from the host layer correlate directly with ProxySQL internal metrics. An RSS spike that coincides with a query_digest_memory growth trend tells you where memory is going without a separate investigation.
- Anomaly detection on query rates and error counters. Netdata’s ML-based anomaly detection flags deviations from established baselines without requiring static thresholds, which matters for ProxySQL metrics where absolute thresholds are environment-dependent.






