Dashboard 2.0 now brings multi-partition support. If your BIG-IPs organize applications into administrative partitions, every one of those pools can now be displayed. If you already have Dashboard 1.8 up and running, 2.0 brings no functional changes other than a re-worked iCall method that no longer causes cluster Sync notifications when the script makes a change. If you don't have partitions then staying on 1.8 is the right move.
Pools from /dmz, /secure, or any other partition appear right alongside your Common pools in the same grid. The grid groups everything by partition automatically: Common pools first, then each partition alphabetically, with your custom pool ordering preserved inside each group.
There is no partition dropdown to manage. The search box is partition aware:
- Type
dmzand the grid shows only pools that reside in the dmz partition - Type
dmz AND webto narrow it to specific web pools within the dmz - Add
NOTto exclude anything you don't want in the view i.e. a pool named 'test-dmz' in a non-dmz partition
If web-pool exists in both /Common and /dmz, the dashboard treats them as the two different pools they are. Status changes, acknowledgments, and history are tracked independently, log entries show the full path so there is no guessing which one flapped, and drag reordering keeps each partition's pools grouped together.
The included discovery tooling finds pools across all partitions and keeps the dashboard's pool list current on its own. New pools show up on the dashboard within a minute of being created; deleted pools disappear just as fast. Want to keep a partition off the dashboard entirely? Add it to the exclusion list and it's gone on the next sync.
Dashboard's iCall Sync script no longer trips the 'Changes Pending' flag on manually synced device groups via a reworked method. The automatically maintained pool lists have been moved to a device-local location that config sync ignores, so the sync status on your clusters only reflects changes a human actually made.
All dashboard components must be upgraded to 2.0; 1.8 and 2.0 components are not compatible.
When an application is degraded, you need to know which pool members are down across all your data centers without logging into each F5 to check. This dashboard answers that question in about ten seconds.
It's a browser-based monitoring application that provides near real-time visibility into F5 BIG-IP pool member status across unlimited sites. It runs entirely from the F5 devices themselves in the dataplane: no external servers, no databases, no agents. Upload seven files, configure eight data groups, apply two iRules, and you're operational in about thirty minutes.
The architecture is distributed rather than centralized. Each F5 site can operate as a Dashboard Frontend (serving the interface and aggregating data), an API Host (providing pool data via JSON endpoints), or both. Sites communicate directly with each other, so there is no central monitoring server to bottleneck and the design scales horizontally without redesign.
Traditional monitoring polls everything, stores everything, and filters when queried. This dashboard inverts that model: the client tells the backend exactly what it needs each cycle. If a search for "sharepoint" shows three matching pools out of two hundred configured, the next poll queries only those three pools, and the backend skips the other 197 entirely.
The BIG-IPs remain stateless. They receive a JSON request, process the requested pools, return JSON, and forget everything. All state tracking happens in the client. The F5 dataplane already handles thousands of decisions per second for production traffic, so checking status on a handful of pools every thirty seconds is negligible overhead.
The entire Dashboard system is fundamentally a sophisticated wrapper around one F5 iRule TCL command:
LB::status pool $pool_name member $ip $port- Executes this command across pools and members at each poll
- Returns status
All JavaScript modules, CSS themes, and iRules exist to make that single status command operationally useful by adding visual presentation, change tracking, and user experience features.
At its core it's a pool status iRule on steroids: query member status β detect changes β display nicely β repeat
The dashboard consists of two components:
- Serves the web interface and static assets
- Handles user authentication via APM
- Provides local pool monitoring for the frontend site
- Proxies requests to remote backend API Hosts
- Exposes JSON API endpoints for pool data
- Performs DNS resolution and member status checks
- Provides health monitoring endpoints
Notional layout of the multi-site topology. Supports multiple Frontends; scales horizontally as needed
Theme1 - AGLight in MACRO mode

Theme2 - Monochrome Grey in micro mode with logger active, alarmed pools, an active search, and actual pool name mode enabled

Theme3 - Amber in MACRO mode with alarmed pool members

3 instances of Dashboard showing 3 sites in 3 tabs of Microsoft Edge with instance site table data isolation
π Installation Guide - Step-by-step setup instructions for both Dashboard Frontend and API Host components
π User Guide - Comprehensive Dashboard User Manual
DNS resolution only supports IPv4 PTR lookups at this time
Dashboard virtual servers and iRules must reside in the /Common partition.
- TMOS 17.x+ series (all versions)
- TMOS 21.x+ series (all versions)
Scalability:
- Tested with 500+ pools per site on lab grade VEs
- Tested with 1000+ pool members on lab grade VEs
- Currently deployed and in operation with various organizations on pre-iSeries appliances, iSeries appliances, and rSeries appliance tenants
The F5 Multisite Dashboard uses a 3-level procedural architecture with automatic memory management and efficient variable scoping. Procedures offer code modularity for easy sharing between Frontend and API Hosts to maintain operational parity for Dashboard components. It became very apparent early in development that standard iRule "monolithic blocks of code" would be unsustainable long term if we wanted the Frontend and API Host to operate identically throughout development and feature tuning. Procedures were the logical solution.
HTTP Request (/api/proxy/pools)
β
βΌ LEVEL 0: Global Scope (HTTP_REQUEST Event)
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β’ Parse headers (X-Selected-Site, X-Need-Pools-*, X-Need-DNS-*)
β β’ Initialize variables: dns_request_cache = {}
β β’ Determine pool filtering and DNS optimization needs
β β’ Call main coordinator procedure
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ LEVEL 1: collect_all_pool_data (Pool Coordinator)
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β’ Receives: pool lists, DNS settings, cache reference
β β’ Decides which pools to process (filtered vs all)
β β’ Loops through each selected pool
β β’ upvar dns_request_cache β Global Scope
β β’ Returns: comma-separated JSON string
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ LEVEL 2: process_single_pool (Individual Pool Handler)
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β’ Receives: single pool name + configuration
β β’ Queries F5: members -list, LB::status for each member
β β’ Builds JSON for each pool member
β β’ Counts up/down/disabled members
β β’ upvar dns_request_cache β Level 1 Scope
β β’ Returns: complete pool JSON object
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ LEVEL 3: resolve_hostname_for_json (DNS Resolution - Optional)
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β’ Called only when DNS resolution needed
β β’ Checks cache first, performs PTR lookup if needed
β β’ Handles IPv4 β d.c.b.a.in-addr.arpa conversion
β β’ upvar dns_request_cache β Level 2 Scope
β β’ Returns: "null" or "\"hostname.domain.com\""
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
The dashboard scopes every request to minimize dataplane impact on the BIG-IP.
Each poll cycle sends X-Need-Pools headers listing only the pools currently visible in the grid. With no search filter active, all pools in datagroup-dashboard-pools are processed as normal. With a filter active showing 4 pools out of 100 configured, only those 4 are sent and the backend processes only those 4. To force a full-site update, clear the search filter.
X-Need-Pools-Count: 2
X-Need-Pools-1: pool1,pool2,/dmz/pool3,pool4,pool5
X-Need-Pools-2: /secure/pool6,pool7Up to 5 pool names per header (canonical form for partitioned pools) and up to 50 numbered headers, for a limit of 250 filtered pools per request. Requests exceeding the cap fall back to full-site processing.
Browsers cannot perform PTR lookups themselves, so hostname resolution is handled by the BIG-IP. It runs outside the normal poll cycle and only when the user clicks Resolve: the dashboard collects the member IPs that don't already have cached hostnames, packages them into X-Need-DNS headers, and sends a one-off request to the selected site. Like polling, resolution respects the search filter and only covers visible pool members.
X-Need-DNS-Count: 2
X-Need-DNS-IPs-1: 192.168.1.1,192.168.1.2,...,192.168.1.64
X-Need-DNS-IPs-2: 192.168.2.1,192.168.2.2Up to 64 IPs per header and up to 50 numbered headers (3,200 IPs maximum), enforced by backend header-count validation.
Resolved hostnames are cached per site in sessionStorage, so duplicate IPs are resolved once, results survive browser refreshes, and known hostnames are never re-queried until the cache is flushed. Use the flush function after PTR record or hostname changes to force a fresh lookup on the next resolve. Each site can independently enable or disable DNS resolution and configure its own resolver endpoints in the iRule.
/api/proxy/pools
{
"hostname": "bigip-hostname",
"timestamp": "YYYY-MM-DD HH:MM:SS",
"debug_enabled": "enabled|disabled",
"instanceId":"inst_timestamp_random",
"pools": [
{
"partition": "Common|partition_name",
"name": "pool_name",
"alias": "user_friendly_name" or null,
"sort_order": number,
"status": "UP|DOWN|DISABLED|UNKNOWN|EMPTY",
"up_members": number,
"down_members": number,
"disabled_members": number,
"total_members": number,
"members": [
{
"ip": "x.x.x.x",
"port": "port",
"status": "up|down|disabled|session_disabled",
"hostname": "resolved-hostname" or null
}
]
}
]
}/api/health
{
"status": "healthy|unhealthy",
"hostname": "bigip-hostname",
"timestamp": "YYYY-MM-DD HH:MM:SS",
"uptime_seconds": number,
"version": "2.0",
"pools_configured": number,
"message": "status description"
}MIT License - see LICENSE file for details.
- This solution is NOT officially endorsed, supported, or maintained by F5 Inc.
- F5 Inc. retains all rights to their trademarks, including but not limited to "F5", "BIG-IP", "LTM", "APM", and related marks
- This is an independent, community-developed solution that utilizes F5 products but is not affiliated with F5 Inc.
- For official F5 support and solutions, please contact F5 Inc. directly
Technical Disclaimer
- This software is provided "AS IS" without warranty of any kind
- The authors and contributors are not responsible for any damages or issues that may arise from its use
- Always test thoroughly in non-production environments before deployment
- Backup your F5 configuration before implementing any changes
- Review and understand all code before deploying to production systems
By using this software, you acknowledge that you have read and understood these disclaimers and agree to use this solution at your own risk.