# AI Agent Skills URL: https://docs.zymtrace.com/ai-assistant/agent-skills ```mdx-code-block import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; ``` # AI Agent Skills & perf engineer subagents **Zymtrace AI Agent Skills** (plus a subagent) let your AI coding assistant profile and optimize the efficiency of your CPU and GPU-accelerated workloads with Zymtrace through the [MCP](../mcp/): find the bottleneck and apply the fix in your source. They also operate the platform itself, installing the backend and profiler, exposing the gateway, upgrading, and troubleshooting. Describe what you want in plain English and the assistant runs the right workflow, confirming each change with you. The skills install from a single source into **Claude Code, OpenAI Codex, and Cursor**. Source and issues: [github.com/zystem-io/zymtrace-skills](https://github.com/zystem-io/zymtrace-skills). Once installed, describe what you want in plain English: ``` "GPU utilization is 90%+ but tokens/sec is flat. Am I kernel-bound or launch-overhead bound?" "Is my vLLM server actually GPU-bound, or is host-side tokenization/sampling the bottleneck?" "My training step has GPU-idle gaps. Find the sync points and D2H copies stalling the pipeline." "Inference latency regressed after the last deploy. What changed in the hot path?" "Rank my GPU workloads by wasted GPU time and apply the highest-ROI fix." ``` ## Available skills | Skill | What it does | |-------|-------------| | `install-zymtrace-backend` | Install the backend on Kubernetes (Helm) or Docker Compose. Handles license, databases, and air-gapped registries. | | `upgrade-zymtrace-backend` | Bump image tags, chart versions, or both, with built-in rollback and verification. | | `expose-zymtrace-backend` | Expose the gateway via NodePort, AWS ALB, NGINX Ingress, or cloud LoadBalancer, TLS included. | | `install-zymtrace-profiler` | Install the profiler on Kubernetes, Docker, or bare-metal. CPU + CUDA GPU profiling (CUDA 12.x+). | | `troubleshoot-zymtrace-backend` | Diagnose "no data appearing", license errors, ingest crashes, slow queries, and storage issues. | | `troubleshoot-zymtrace-profiler` | Diagnose agent-side failures: CrashLoopBackOff, OOMKilled, NVML missing, PC sampling, license rejected. | | `configure-zymtrace-mcp` | Connect your assistant to the Zymtrace MCP server so you can analyze profiles with natural-language queries. | | `optimize-cpu-workloads` | Find what's eating CPU on a CPU deployment: rank the top consumers (process / function / container / host / pod), read the flamegraph, name the pattern, and apply the fix in your source. The default when you name an entity with no accelerator hint. | | `optimize-gpu-workloads` | Analyze a GPU/CUDA workload or inference/training job (vLLM, SGLang, Triton, TensorRT-LLM): pull GPU + matching CPU flamegraphs, find the hot kernel or host-side bottleneck, and apply the fix. | | `optimize-memory-allocation` | Analyze JVM memory allocation profiles (Java only): find what allocates the most and why GC is busy, then apply the fix. Enables allocation profiling first if it isn't on yet. | ## Available subagents :::note Invoke the subagent by name from any assistant that supports subagents. ::: | Subagent | What it does | |----------|-------------| | `zymtrace-perf-engineer` | Autonomous, hands-off investigation that **fixes, not just analyzes**. Ranks the top consumers or drills into a named entity, pulls its metrics, then the CPU flamegraph (and the GPU flamegraph for GPU workloads), recaps, then locates the hot frame in your source and applies the fix (asking for the path if it isn't local), all without stopping to confirm each step. Run several in parallel to triage multiple workloads at once. | ## Install the plugin ```bash claude plugin marketplace add zystem-io/zymtrace-skills claude plugin install zymtrace@zymtrace-skills ``` Skills become available as `/zymtrace:install-zymtrace-backend`, `/zymtrace:optimize-gpu-workloads`, and so on. Run `/skills` (and `/agents` for the subagent) to see what loaded. ```bash codex plugin marketplace add zystem-io/zymtrace-skills ``` Then run `/plugins` in Codex and install **Zymtrace** from the marketplace. The skills load on your next thread. 1. Open **Dashboard → Settings → Plugins**. 2. Under **Team Marketplaces**, click **Import**. 3. Paste the repository URL `https://github.com/zystem-io/zymtrace-skills` and continue. 4. Review the parsed **Zymtrace** plugin, set access as you like, and save. ### Other tools The plugin ships in the Claude Code plugin format, which several other assistants also read. The same marketplace works with them: | Tool | Install | |------|---------| | Augment (Auggie CLI) | `auggie plugin marketplace add zystem-io/zymtrace-skills` → `auggie plugin install zymtrace@zymtrace-skills` | | GitHub Copilot CLI | `copilot plugin install zystem-io/zymtrace-skills:zymtrace` | For any other MCP-capable assistant (Claude Desktop, Cline, Windsurf, Amazon Q Developer, VS Code + Copilot), you don't need the plugin to analyze profiles. Connect the [MCP endpoint](#connecting-to-zymtrace) directly. If the tool supports skills or rules, you can also copy the Markdown skill files from [the repo's `zymtrace/skills/`](https://github.com/zystem-io/zymtrace-skills/tree/main/zymtrace/skills) into its skills/rules directory. ## Connecting to Zymtrace The `optimize-*` skills and the `zymtrace-perf-engineer` subagent query live profiles through the Zymtrace **MCP server**; `configure-zymtrace-mcp` automates the connection. This section is the reference. ### MCP endpoint Your MCP endpoint is your Zymtrace gateway URL + `/mcp`: ``` https://zymtrace.your-company.com/mcp ``` Zymtrace is self-hosted, so this URL is specific to your deployment. See [Configure MCP](../mcp/configure-mcp) for the per-tool setup (Claude Code, Codex, Cursor). ### Authentication | Deployment | What's needed | |------------|---------------| | Service-token auth **off** (default) | Nothing: the connection is open within any network that can reach the gateway. | | Service-token auth **on** | A Zymtrace **service token**, passed as a `Bearer` header. Generate one in the Zymtrace UI (Settings → Service Tokens); see [Service Tokens](../authentication/service-tokens). | The token is always referenced from an environment variable (e.g. `$ZYMTRACE_MCP_TOKEN`, or `${env:ZYMTRACE_MCP_TOKEN}` in Cursor's `mcp.json`); it is never written to disk in plaintext or pasted into the conversation. For production exposures behind an ALB / Ingress, service-token auth is recommended. --- # Configure AI Assistant URL: https://docs.zymtrace.com/ai-assistant/configure-ai-assistant ```mdx-code-block import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; ``` # Configure Zymtrace AI Assistant The AI Assistant feature enables AI-powered analysis capabilities directly in the Zymtrace UI. You can configure one or multiple AI providers to get intelligent insights about your performance data. ## Supported Providers Zymtrace supports the following AI providers: | Provider | Get API Key | |----------|-------------| | **Anthropic Claude** | [console.anthropic.com](https://console.anthropic.com/) | | **Google Gemini** | [aistudio.google.com/apikey](https://aistudio.google.com/apikey) | | **OpenAI** | [platform.openai.com/api-keys](https://platform.openai.com/api-keys) | | **Custom LLM** | Any OpenAI-compatible chat completions endpoint (e.g., Groq, Together AI, self-hosted models) | ## Key Advantages **Flexible AI Integration**: Zymtrace provides flexibility for custom AI inferences, allowing you to integrate any OpenAI-compatible LLM endpoint. This includes popular providers like Groq, Crusoe, Together AI, and self-hosted models. This means you can: - Use self-hosted models for enhanced privacy and control - Leverage enterprise-grade AI infrastructure - Maintain data sovereignty with on-premises deployments - Customize models for domain-specific performance analysis - Reduce costs by using optimized, specialized models Whether you're using cloud-hosted LLMs, on-premises AI infrastructure, or specialized models fine-tuned for your specific use case, Zymtrace seamlessly integrates with your preferred AI setup. ## Prerequisites - Zymtrace backend version `25.12.3` or later - API key from at least one supported AI provider ## Configuration ### Enable AI Assistant with Helm [![Artifact Hub: Zymtrace backend](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/zymtrace)](https://artifacthub.io/packages/helm/zymtrace/backend?modal=values) :::info Helm Chart Source The Helm chart source code is available on GitHub: [zystem-io/zymtrace-charts](https://github.com/zystem-io/zymtrace-charts/tree/main/charts/) ::: To enable the AI Assistant, set `aiAssistant.enabled=true` and provide API key(s) for your chosen provider(s). You can configure one or multiple providers: ```bash helm upgrade --install backend zymtrace/backend \ --namespace zymtrace \ --set aiAssistant.enabled=true \ --set aiAssistant.anthropic.apiKey="$ANTHROPIC_API_KEY" \ --set aiAssistant.gemini.apiKey="$GEMINI_API_KEY" \ --set aiAssistant.openai.apiKey="$OPENAI_API_KEY" \ --reset-then-reuse-values \ --debug \ --atomic ``` :::tip You only need to configure the providers you want to use. For example, to use only Anthropic Claude, simply omit the other `--set` flags. ::: ### Using a Values File For more maintainable configuration, use a custom values file: ```yaml title="custom-values.yaml" aiAssistant: enabled: true # Anthropic Claude - https://console.anthropic.com/ anthropic: apiKey: "sk-ant-api03-..." # Google Gemini - https://aistudio.google.com/apikey gemini: apiKey: "AIzaSy..." # OpenAI - https://platform.openai.com/api-keys openai: apiKey: "sk-proj-..." # Custom LLM - Any OpenAI-compatible endpoint (e.g., Groq, Together AI, self-hosted models) customLLM: url: "https://your-llm-endpoint.com/v1/chat/completions" apiKey: "your-custom-api-key" models: "model-1,model-2,model-3" ``` Then install or upgrade with: ```bash helm upgrade --install backend zymtrace/backend \ --namespace zymtrace \ -f custom-values.yaml \ --debug \ --atomic ``` :::tip Using Kubernetes Secrets For production deployments, consider using external secrets management (like External Secrets Operator or Sealed Secrets) rather than storing API keys directly in values files. ::: ### Enable AI Assistant with Docker Compose Add the following environment variables to your `docker-compose.yml` file for the `web` service: ```yaml title="docker-compose.yml" services: web: # ... other configuration ... environment: # Enable one or more AI providers WEB__ASSISTANT__API_KEYS__ANTHROPIC: "${ANTHROPIC_API_KEY}" WEB__ASSISTANT__API_KEYS__GEMINI: "${GEMINI_API_KEY}" WEB__ASSISTANT__API_KEYS__OPENAI: "${OPENAI_API_KEY}" # Custom LLM configuration (e.g., Groq, Together AI, self-hosted models) WEB__ASSISTANT__CUSTOM_LLM__URL: "${CUSTOM_LLM_URL}" WEB__ASSISTANT__CUSTOM_LLM__API_KEY: "${CUSTOM_LLM_API_KEY}" WEB__ASSISTANT__CUSTOM_LLM__MODELS: "${CUSTOM_LLM_MODELS}" ``` Then set the environment variables before starting the containers: ```bash export ANTHROPIC_API_KEY="sk-ant-api03-..." export GEMINI_API_KEY="AIzaSy..." export OPENAI_API_KEY="sk-proj-..." docker compose up -d ``` Alternatively, create a `.env` file in the same directory as your `docker-compose.yml`: ```bash title=".env" ANTHROPIC_API_KEY=sk-ant-api03-... GEMINI_API_KEY=AIzaSy... OPENAI_API_KEY=sk-proj-... CUSTOM_LLM_URL=https://your-llm-endpoint.com/v1/chat/completions CUSTOM_LLM_API_KEY=your-custom-api-key CUSTOM_LLM_MODELS=model-1,model-2,model-3 ``` :::warning Never commit `.env` files containing API keys to version control. Add `.env` to your `.gitignore` file. ::: ## Advanced Configuration ### MCP Server Configuration Configure MCP (Model Context Protocol) servers that the AI Assistant can use for enhanced profiling analysis or other tasks. ```yaml title="custom-values.yaml" aiAssistant: enabled: true mcpServers: # Optional: Add additional MCP servers - name: custom-mcp endpoint: "http://my-mcp-server:8080/mcp" authToken: "my-secret-token" ``` If no authentication is required, comment out or omit `authToken`. The MCP servers configuration is provided as a JSON array via environment variable: ```yaml title="docker-compose.yml" services: web: environment: WEB__ASSISTANT__MCP_SERVERS: '[{"name":"custom-mcp","endpoint":"http://my-mcp-server:8080/mcp","auth-token":"secret"}]' ``` **JSON Format:** ```json [ { "name": "custom-mcp", "endpoint": "http://my-mcp-server:8080/mcp", "auth-token": "my-secret-token" } ] ``` **Fields:** - `name`: Identifier for the MCP server - `endpoint`: Full URL to the MCP server endpoint - `auth-token`: Authentication token ## Configuration Reference ### API Keys and Providers | Parameter | Description | Default | |-----------|-------------|---------| | `aiAssistant.enabled` | Enable AI Assistant features (Helm only) | `false` | | `aiAssistant.anthropic.apiKey` | Anthropic Claude API key | - | | `aiAssistant.gemini.apiKey` | Google Gemini API key | - | | `aiAssistant.openai.apiKey` | OpenAI API key | - | | `aiAssistant.customLLM.url` | Custom LLM endpoint URL (OpenAI-compatible) | - | | `aiAssistant.customLLM.apiKey` | Custom LLM API key | - | | `aiAssistant.customLLM.models` | Comma-separated list of available custom models | - | ### Advanced Configuration | Parameter | Description | Default | |-----------|-------------|---------| | `aiAssistant.mcpServers` | Array of MCP server configurations (Helm only) | None | ## Verify Configuration After deploying, you can verify the AI Assistant is enabled: 1. Open the Zymtrace UI in your browser 2. Navigate to any flamegraph or performance view 3. Look for the AI Assistant icon or panel 4. Try asking a question about your performance data ## Troubleshooting ### AI Assistant not appearing in UI - Verify `aiAssistant.enabled` is set to `true` (Helm deployments) - Check that at least one API key is configured - Ensure you're using Zymtrace backend version `25.12.0` or later - Check the web service logs for any API key validation errors: ```bash kubectl logs -l app=zymtrace-web -n zymtrace ``` ### API errors or rate limiting - Verify your API key is valid and has not expired - Check your API provider's usage dashboard for rate limits - Ensure your API key has the necessary permissions/scopes ### Connection issues - If using a proxy, ensure the web service can reach the AI provider's API endpoints - Check network policies if running in a restricted Kubernetes environment ## Security Considerations - **API keys are stored as Kubernetes Secrets** when using Helm, providing encryption at rest (if enabled in your cluster) - **Rotate API keys periodically** according to your organization's security policies - **Use separate API keys** for development and production environments - **Monitor API usage** through your provider's dashboard to detect any anomalies --- # Overview URL: https://docs.zymtrace.com/ai-assistant/ # AI Assisted Profiling Zymtrace provides context-aware AI analysis for your CPU and GPU profiling data. Ask questions in natural language, get actionable optimization recommendations, and turn complex flamegraphs into concrete performance improvements. ## Reducing Mean Time to Dopamine Flamegraphs are powerful but dense. Even experienced engineers can spend hours staring at call stacks before finding the optimization that matters. GPU profiles add another layer: CUDA kernels, tensor operations, memory transfers, and framework overhead all interleave in ways that require specialized knowledge to untangle. This skill gap slows everyone down. Junior engineers struggle to know where to start. Senior engineers spend time on interpretation instead of implementation. And the dopamine hit of actually shipping a performance fix gets delayed by hours or days of analysis. AI analysis compresses this loop. Describe what you're investigating, get targeted recommendations, and move straight to the fix. The goal isn't to replace profiling expertise—it's to make that expertise accessible faster, so more of your team can turn profiles into shipped improvements. ## Context-Aware Analysis Unlike generic AI assistants, Zymtrace's AI integration understands your profiling context: - **Profile metadata**: Hosts, Namespaces, Deployments, Pods, containers and main executables - **Flamegraph structure**: Call stacks, resource attribution, and function-level timing - **GPU-specific patterns**: CUDA kernel launches, memory copy operations, tensor core utilization - **Historical context**: How current profiles compare to previous captures This context enables precise answers. When you ask "why is inference slow?", the AI examines your actual profile data—not just general ML performance advice.
AI Assistant
## What You Can Do - **Identify bottlenecks** — Point to hotspots in your flamegraph and get explanations of why that code path is expensive, whether it's expected, and what alternatives exist. - **Get optimization recommendations** — Receive specific suggestions: batching strategies, memory layout changes, kernel fusion opportunities, or framework configuration tweaks. - **Investigate regressions** — Compare profiles across deployments and understand what changed. The AI highlights meaningful differences versus noise. - **Learn as you go** — Ask follow-up questions about GPU architecture, framework internals, or profiling methodology. Build intuition alongside getting answers. ## Supported Providers Configure your preferred AI provider: - Anthropic Claude - Google Gemini - OpenAI GPT - Custom inference endpoints ## Flexible Inference Options Zymtrace supports both **cloud-hosted** and **self-hosted** AI inferences, giving you complete control over your AI infrastructure: **Cloud Providers**: Use managed AI services from Anthropic, Google, or OpenAI for immediate setup and global scale. **Self-Hosted/Custom**: Deploy your own models or use specialized providers like Groq, Together AI, or any OpenAI-compatible endpoint. Perfect for: - Enterprise data governance and compliance - Custom fine-tuned models for domain-specific analysis - Cost optimization with specialized hardware - Air-gapped or restricted environments You bring your own API key. Your profiling data is sent to the AI provider only when you explicitly request analysis. Zymtrace doesn't share profile data with third parties by default. :::tip Prefer your IDE? If you'd like AI-powered profiling analysis directly in your development environment, check out [MCP integration](../mcp/configure-mcp). Connect Zymtrace to Claude Desktop, Cursor, Cody, or any MCP-compatible client. ::: ## Get Started

🤖 In-UI Assistant

Enable AI analysis in the dashboard

Configure AI Assistant →

🔌 MCP Integration

Connect to your IDE's AI assistant

Configure MCP →
--- # Using the AI Assistant URL: https://docs.zymtrace.com/ai-assistant/using-ai-assistant # Using the AI Assistant Zymtrace has two built-in AI assistants: | | General AI Assistant | Flamegraph AI Assistant | | --- | --- | --- | | Where | **AI Assistant** in the sidebar | **AI Assistant** button above a CPU or GPU flamegraph | | Scope | All profiling data in the project, through the [Zymtrace MCP server](../mcp/index.md) | The flamegraph you're viewing | | Best for | Open questions across your fleet: "Where is most of the CPU time going?" | Explaining and optimizing a specific profile | Both use the AI provider configured in [Configure AI Assistant](./configure-ai-assistant.md). ## General AI Assistant The general AI Assistant is a built-in client for the [Zymtrace MCP server](../mcp/index.md). It works like connecting Claude Code or another MCP client from your terminal, with no setup. Ask a question and it finds the relevant workloads, pulls the profiling data it needs, and uses existing [recommendations](../features/recommendations.md) where they apply. General AI Assistant answering where CPU time is going across the fleet, with a ranked table of workloads and the cores each one uses To start a conversation: 1. Open **AI Assistant** in the sidebar. 2. Choose an analysis mode: **General CPU Performance Analysis**, **General GPU Performance Analysis**, or **General Memory Allocation Analysis**. 3. Type a question, or pick one of the suggested prompts. You can switch models from the model picker, attach files with **+**, and start over with **New Chat**. ## Flamegraph AI Assistant The flamegraph AI Assistant analyzes the flamegraph on screen. Click **AI Assistant** above a CPU or GPU flamegraph to open it next to the flamegraph. The current flamegraph is attached to your message as **Flamegraph Data**. CPU flamegraph with the AI Assistant panel open, showing suggested prompts and Flamegraph Data attached to the message box Ask a question or pick a suggestion such as **Analyze the flamegraph**. :::tip Prefer your IDE or terminal? Connect your own AI assistant to the same data with the [Zymtrace MCP server](../mcp/configure-mcp.md). ::: --- # Zymtrace RESTful APIs URL: https://docs.zymtrace.com/api/ # Zymtrace RESTful APIs Every Zymtrace instance ships with a built-in **API Explorer**, an interactive REST API reference you can use to explore endpoints, read documentation, and send live requests directly from your browser. ## Opening the API Explorer Click **API Explorer** in the top navigation bar of your Zymtrace instance. ![API Explorer](/img/api.png) The API is versioned under `/public/api/v1/` and organized into the following categories: | Category | Description | |---|---| | `flamegraph` | Generate JSON-format flamegraphs for a project | | `metrics` | Query CPU and GPU profiling metrics | | `aggregate-stats` | Aggregate statistics by container, host, executable, and more | | `aggregate-events` | Aggregate profiling events across your fleet | | `events` | Fetch raw profiling events | | `projects` | Create and manage projects | | `users` | Manage user accounts and profiles | | `auth` | Authentication and session management | | `roles` | Role management | | `permissions` | Permission management | | `service-tokens` | Create and revoke API service tokens | | `config` | System configuration | | `secrets` | Secret management | | `agent-config` | Profiler agent configuration | | `audit` | Audit logs | | `license` | License information | ## Authentication If authentication is enabled on your instance, API requests require a bearer token. Generate a service token from **Profile → Account → Service Tokens** and use it as the bearer token. See [Service Tokens](../authentication/service-tokens.md) for step-by-step instructions. Enter the token in the **Authorization** field at the top of the API Explorer, then use the **Test Request** button on any endpoint to send a live request. ## Example Request Generate a JSON flamegraph for a project: ```bash curl https://demo.zymtrace.com/public/api/v1/project/123e4567-e89b-12d3-a456-426614174000/flamegraph/json \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer ' \ --data '{ "filter": { "end_ts": 4102444800000, "event_kind": "on_cpu", "frame_mask": { "patterns": [] }, "query": null, "start_ts": 946684800000 }, "num_samples": null, "num_stat_buckets": null, "synthetic_frames": [ "deployment" ] }' ``` A successful response returns the flamegraph data as JSON. --- # Platform Architecture URL: https://docs.zymtrace.com/architecture # Platform Architecture The Zymtrace platform is self-hosted, deployed entirely within your infrastructure, whether that's cloud, on-prem, or hybrid. This gives you full control over your data and setup. The platform consists of two main components: 1. **Zymtrace profiler**: An eBPF-based agent installed on each machine running your applications. It collects CPU and GPU performance profiles, and host metrics, all via eBPF — polling `/proc` is expensive due to repeated filesystem reads. Data is sent to the backend using the custom stateful protocol designed for the high throughput demands of fleet-wide profiling, or the stateless OTel Profiles protocol. [Learn more →](#wire-protocol). 2. **Backend services**: The backend services store, process, and analyze performance profiles and metrics. All our core backend services are written in **Rust** :crab:. The front-end is a combination of **ReactJS** and **WASM**. All data is also accessible via a [REST API](#api) and [MCP](/mcp/configure-mcp), directly from your Zymtrace instance. The diagram below depicts a high-level architecture of the components: Architecture Architecture :::info **Need a hosted Zymtrace backend?** We can provision a dedicated cloud deployment for you. Email us at [team@zymtrace.com](mailto:team@zymtrace.com) or [request access here](https://www.zymtrace.com/getstarted) ::: ## Components overview ### Zymtrace profiler The [Zymtrace profiler](./install/profiler/install-profiler.md) runs on each node, deployed either as a Kubernetes DaemonSet or as a standalone binary on a standard VM. It collects performance profiles of resource-intensive processes on the node, aggregates and compresses them, and sends them to the backend via gRPC. TLS is supported by default, with an option to disable it if needed. Here's a more detailed description of how the profiler works: Agent architecture Agent architecture 1. **Unwinder eBPF programs** are loaded into the kernel. 2. The kernel verifies the safety of the BPF program. If approved, the program is attached to probes and triggered upon specific events. 3. The eBPF programs collect data and pass it to userspace via **maps**. 4. The agent retrieves the collected data from maps. This data includes process-specific and interpreter-specific meta-information, which helps the eBPF unwinder programs perform mixed-stack unwinding across different languages (e.g., Python calling into C libraries). 5. The agent pushes stack traces, metrics, and metadata to the **Zymtrace gateway service** for routing to the appropriate backend services for analysis. 6. Easily identify & optimize the most inefficient functions across your entire infrastructure. ### Zymtrace GPU profiler GPU utilization, temperature, power: those metrics are easy to collect, but they don't explain the inner workings of the GPU or why it is idling. That's why we didn't start with [NVML](https://developer.nvidia.com/management-library-nvml) or [DCGM](https://developer.nvidia.com/dcgm) metrics. We started deeper, closer to the hardware. That's where real insight lives, not just in metrics. Zymtrace profiles NVIDIA CUDA and AMD ROCm workloads at the kernel level and correlates GPU activity with the CPU traces that launched those kernels, without requiring recompilation. For NVIDIA workloads, it also disassembles CUDA SASS mnemonics and exposes GPU stall reasons. AWS Neuron accelerators, including Inferentia and Trainium, are also supported. Support for Google TPUs and Cerebras is coming soon. **Learn more:** [GPU Profiler deep dive →](/gpu-profiler-architecture) ## Zymtrace backend services The Zymtrace backend is designed to store, process and visualize profiling data efficiently. Below is an overview of the key backend services and their roles: #### **gateway service** The `gateway` service acts as the main entry point for all user requests, using Envoy Proxy for request routing and load balancing. It routes requests to the appropriate backend services (UI, ingest, symDB) and handles mTLS when configured. #### **UI service** The `UI` service serves the web-based user interface for Zymtrace, built with ReactJS and WASM. Users access the UI through the gateway service, which routes web requests to this service. #### **ingest service** The `ingest` service receives profiling data and metrics from the Zymtrace profiler via the gateway service, and stores them in ClickHouse for ultra-fast querying and analysis. In addition to CPU and GPU profiling data, the ingest service handles metrics from: - **GPU performance metrics**: utilization, memory, temperature, SM efficiency, SM occupancy, Tensor Core utilization, PCIe throughput - **AWS Neuron**: metrics from Inferentia and Trainium accelerators - **NVIDIA Dynamo / Triton**: inference server metrics - **vLLM**: LLM serving metrics - **SGLang**: structured generation serving metrics - **Host metrics**: CPU, memory, disk throughput and latency, collected via eBPF rather than polling `/proc` — polling `/proc` is expensive due to repeated filesystem reads Every metric data point is correlated to the underlying CPU and GPU profiles, so you can move directly from a metric anomaly to the exact code path responsible. Data is received over the [wire protocol](#wire-protocol). #### **symDB service** The `symDB` service handles symbol resolution upon request. It retrieves native symbols stored in S3/Minio or fetches them from the `global symbolization` service. This service is critical for converting raw profiling data into meaningful stack traces by resolving both native and interpreted symbols. **Learn more:** [Symbolization deep dive →](./symbols/symbolization.md) #### **identity service** Zymtrace provides the granularity to segregate your profiling data into different projects within an organization. The identity service currently manages these projects. It associates incoming profiling data from the `ingest` service with the correct project, laying the foundation for future user authentication and role-based access control. #### **worker service** The `worker` service currently handles the [Slack integration](./integrations/slack.md). It is not enabled by default, see [here](./install/backend/helm-docker.md#worker-service-beta) for how to enable it. :::note The `worker` service is currently in beta. To help ensure a smooth rollout, it is being released with a limited feature set. More functionality will be added soon. ::: ### Wire protocol By default, the profiler communicates with the backend using a custom stateful wire protocol that is **6x more compute-efficient** than the OpenTelemetry Profiles proto. Because the protocol is stateful, the agent only transmits new information rather than re-sending context with every payload, which is what the stateless OTel proto requires. This keeps agent CPU overhead and network egress minimal at fleet scale. You can switch between the custom Zymtrace protocol and OTel Profiles protocol at any time without losing data. See [Profiler ENV & CLI Args](/install/profiler/profiler-configuration). The OTel Profiles specification [recently reached alpha](https://opentelemetry.io/blog/2026/profiles-alpha/); the Zymtrace founders were core contributors, part of the team that donated the profiler and helped define the spec inside the OpenTelemetry project. Zymtrace is fully OTel-compliant for resource attributes and metadata. All profiling data is tagged using OpenTelemetry resource attributes, usable directly in filters and queries. :::tip To switch to the OTel Profiles protocol, set `-zymtrace-protocol=false`. See [Profiler ENV & CLI Args](/install/profiler/profiler-configuration) for all configuration options. ::: ### Storage | Storage | Purpose | | |---|---|---| | **ClickHouse** | Profiling events and analytics | [Guide](/databases/clickhouse) | | **PostgreSQL** | User data, metadata, project configuration | [Guide](/databases/postgres) | | **S3 / MinIO** | Native debug symbols | [Guide](/databases/object-storage) | ### MCP Zymtrace implements the [Model Context Protocol](https://modelcontextprotocol.io/), letting AI agents in your IDE query profiling data using natural language. [Learn more →](/mcp/configure-mcp) ```bash claude mcp add zymtrace \ --transport http \ https://your-zymtrace-instance.com/mcp \ --header "Authorization: Bearer YOUR_TOKEN_HERE" ``` ### API Every piece of data in Zymtrace is accessible via a REST API. Click **API Explorer** inside your Zymtrace instance to browse and test every endpoint interactively. ![Zymtrace API Explorer showing the flamegraph endpoint with a curl example](/img/api.png) ## Get started ### Zymtrace backend (Self-Hosted) Refer to our [self-hosted installation guide](/install/backend/helm-docker) for detailed instructions. ### Zymtrace profiler Refer to the [profiler host agent installation guide](/install/profiler/install-profiler) for more details. --- # Admin CLI URL: https://docs.zymtrace.com/authentication/admin-cli # Admin CLI The identity service ships with `zymtrace-cli`, an administrative tool for managing users and roles directly, without logging in to the UI. Use it if you: - lost your admin password - misconfigured OIDC provider - have an account without the right roles If you have shell access to the identity service (SSH to the host, or `kubectl exec` into the pod), you can restore access with a few commands. All operations are recorded in the audit log with `origin=cli`. ## Running the CLI In Kubernetes deployments, execute the CLI inside the identity pod: ```bash kubectl exec -it -n your-zymtrace zymtrace-identity-XXX-XX -- bin/zymtrace-cli --help ``` Find your identity pod name with `kubectl get pods -n | grep identity`. ## Restoring access A typical lockout recovery: ```bash # 1. Find your account zymtrace-cli identity user list --query admin@company.com --include-roles # 2. Reset its password (prompts for the new password with input hidden) zymtrace-cli identity user reset-password --user-id # 3. Make sure it has the System Admin role zymtrace-cli identity roles set --user-id --admin ``` Or create a fresh admin account from scratch: ```bash # Prompts for the password with input hidden zymtrace-cli identity user create --email recovery@company.com zymtrace-cli identity roles set --user-id --admin ``` :::info OIDC deployments When OIDC is enabled, local login additionally requires the `auth:local:login` permission. The built-in `System Admin` role includes it (`**:*`), so an account recovered with `roles set --admin` can always log in locally at `https://zymtrace.company.com`. ::: ## Command reference All commands accept `-e/--endpoint ` (default `http://127.0.0.1:7829`, which is correct when running inside the identity pod; otherwise, you need to specify the correct address). ### Create a user ```console $ zymtrace-cli identity user create --email jane@company.com --password 'secure-password' --given-name Jane --family-name Doe User 01a0345e-3959-7162-8336-ad512287d145 email: jane@company.com given_name: Jane family_name: Doe created_at: 2026-08-24T15:23:05.433+00:00 updated_at: 2026-08-24T15:23:05.048296+00:00 ``` Omit `--password` to read it from stdin: on a TTY you get an interactive prompt with input hidden; otherwise it is read from the pipe: ```bash echo 'secure-password' | zymtrace-cli identity user create --email jane@company.com ``` New users start with only the default `Default Project Viewer` role; grant more with `zymtrace-cli identity roles set`. ### List users ```console $ zymtrace-cli identity user list --query jane --include-roles User 01a0345e-3959-7162-8336-ad512287d145 email: jane@company.com given_name: Jane family_name: Doe created_at: 2026-08-24T15:23:05.433+00:00 updated_at: 2026-08-24T15:23:05.048296+00:00 roles: Default Project Viewer Page 0: 1 users (1 total, 1 pages) ``` Other filters: `--org-id `, `--page N`, `--page-size N` (max 100). ### Reset a password Interactively, with input hidden: ```bash zymtrace-cli identity user reset-password --user-id 01a0345e-3959-7162-8336-ad512287d145 ``` Or non-interactively, via `--password` or a pipe (note that both can leak the password into shell history or process listings): ```bash zymtrace-cli identity user reset-password --user-id 01a0345e-3959-7162-8336-ad512287d145 --password 'new-password' echo 'new-password' | zymtrace-cli identity user reset-password --user-id 01a0345e-3959-7162-8336-ad512287d145 ``` ### List roles ```console $ zymtrace-cli identity roles list --name "System Admin" Role 00000000-0000-0000-0000-000000000001 name: System Admin is_locked: true users: 13 perms: **:* 1 roles ``` Filters: `--org-id `, `--user-id ` (only roles assigned to that user), `--name ` (exact match, repeatable). Without filters, all roles are listed. ### Set a user's roles Replaces the user's entire role assignment with the given set. Role names are validated before anything is changed: ```console $ zymtrace-cli identity roles set --user-id 01a0345e-3959-7162-8336-ad512287d145 --admin --role "System Viewer" User 01a0345e-3959-7162-8336-ad512287d145 email: jane@company.com ... roles: System Admin, System Viewer ``` `--admin` is shorthand for including the built-in `System Admin` role. Unknown role names fail without modifying anything: ```console $ zymtrace-cli identity roles set --user-id 01a0345e-... --role "Nonexistent Role" Error: roles not found: Nonexistent Role ``` Pass no roles to revoke everything: ```bash zymtrace-cli identity roles set --user-id 01a0345e-3959-7162-8336-ad512287d145 ``` --- # Advanced Configuration URL: https://docs.zymtrace.com/authentication/advanced-config # Advanced Configuration This guide covers advanced authentication configuration options that apply to both local and OIDC authentication methods. ## Cookie Configuration Cookie settings control how authentication sessions are managed in Zymtrace. These settings apply when using `local` or `oidc` authentication. [![Artifact Hub :zymtrace backend](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/zymtrace)](https://artifacthub.io/packages/helm/zymtrace/backend?modal=values)
custom-values.yaml ```yaml auth: cookie: # Refresh token cookie lifespan (in seconds) # Default: 31536000 (1 year) refreshMaxAgeSec: 31536000 # Path for refresh token cookie refreshPath: / # Login/access token cookie lifespan (in seconds) # Default: 60 (1 minute) loginMaxAgeSec: 60 # Path for login cookie (typically /api/v1/oauth2 for OIDC) loginPath: / # SameSite cookie attribute: "strict", "lax", or "none" sameSite: lax # Secure flag (requires HTTPS) secure: true # HttpOnly flag (prevents JavaScript access) httpOnly: true # OIDC cookie TTL during OAuth flow (in seconds) cookieTtlSec: 60 ```
:::tip Cookie Security - Always set `secure: true` in production (requires HTTPS) - Keep `httpOnly: true` to prevent XSS attacks - Use `sameSite: lax` or `sameSite: strict` for CSRF protection ::: ## Token Validation Token validation settings control how JWT tokens are verified throughout the system. These settings apply to both `local` and `oidc` authentication. [![Artifact Hub :zymtrace backend](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/zymtrace)](https://artifacthub.io/packages/helm/zymtrace/backend?modal=values)
custom-values.yaml ```yaml auth: validation: # List of valid token issuers (optional) # If empty or not specified, auto-derived from ingress host # Example: If your ingress host is company.example.com, issuer will be https://company.example.com issuers: [ ] # Or specify explicitly: # issuers: # - "https://zymtrace.company.com" # List of valid token audiences (optional) # If empty or not specified, defaults to ["zymtrace"] audiences: [ ] # Or specify explicitly: # audiences: # - "domain" # - "some-api" # Signing keys for token verification (REQUIRED) keys: # Option A: Pass keys inline privateKey: | -----BEGIN PRIVATE KEY----- ... -----END PRIVATE KEY----- publicKey: | -----BEGIN PUBLIC KEY----- ... -----END PUBLIC KEY----- # Option B: Reference an existing Kubernetes Secret (recommended for production) # privateKeySecretName: "zymtrace-signing-keys" # privateKeySecretKey: "private-key" # key name inside the secret # publicKeySecretName: "zymtrace-signing-keys" # publicKeySecretKey: "public-key" # key name inside the secret ```
:::tip Using an existing Kubernetes Secret If you manage secrets externally (e.g. via AWS Secrets Manager, Vault, or Sealed Secrets), use `privateKeySecretName` and `publicKeySecretName` to reference them directly instead of passing key material inline. First, create the Kubernetes Secret (run once before deploying): ```bash kubectl create secret generic zymtrace-signing-keys \ --namespace zymtrace \ --from-literal=private-key="-----BEGIN PRIVATE KEY----- -----END PRIVATE KEY-----" \ --from-literal=public-key="-----BEGIN PUBLIC KEY----- -----END PUBLIC KEY-----" ``` Then reference it in your values file: ```yaml auth: validation: keys: privateKeySecretName: "zymtrace-signing-keys" privateKeySecretKey: "private-key" publicKeySecretName: "zymtrace-signing-keys" publicKeySecretKey: "public-key" ``` The chart injects the key material from the named secret at deploy time, so the values file never contains sensitive key material. If `privateKeySecretName`/`publicKeySecretName` are not set, the chart falls back to the inline `privateKey`/`publicKey` values. **The chart does not pre-check whether the referenced secret exists** — doing so would require cluster read privileges at render time. If the secret is missing, the pods will fail to start. Always verify your deployment is healthy after applying: ```bash kubectl get pods -n zymtrace ``` ::: :::tip Auto-Derived Values Most deployments can leave `issuers` and `audiences` empty to use auto-derived defaults: - **Issuers**: Automatically set to your ingress host URL (e.g., `https://company.example.com`) - **Audiences**: Automatically set to `["zymtrace"]` Only specify these explicitly if you know why you need it. ::: ## OIDC Configuration By default, `scopes` and `extraParams` are configured to support the usual GCP authentication flow. If you're using a different OIDC provider, you can customize the configuration using the following example. [![Artifact Hub :zymtrace backend](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/zymtrace)](https://artifacthub.io/packages/helm/zymtrace/backend?modal=values)
custom-values.yaml ```yaml auth: type: "oidc" oidc: provider: # REQUIRED: OAuth2 client credentials from your OIDC provider clientId: "your-client-id" clientSecret: "your-client-secret" # REQUIRED: Your OIDC provider's issuer URI # Examples: # Google: https://accounts.google.com # Okta: https://dev-123456.okta.com # Azure AD: https://login.microsoftonline.com/{tenant-id}/v2.0 issuerUri: "https://accounts.google.com" # REQUIRED: Redirect URI after authentication # Must match what's registered in your OIDC provider # Auto-derived from ingress host if not specified redirectUri: "https://company.example.com/api/v1/oauth2/callback" # OAuth2 scopes to request scopes: - openid - email - profile # Additional OAuth2 parameters (optional) # These are passed as query parameters to the OIDC provider extraParams: - prompt=consent # Force consent screen on Google - access_type=offline # Request refresh token from Google ```
## Branding You can customize the text your users see when authenticating with Zymtrace.
custom-values.yaml ```yaml auth: info: displayName: "my custom zymtrace deployment" pictureUri: null ```
--- # Basic Authentication URL: https://docs.zymtrace.com/authentication/basic-auth # Basic Authentication Basic authentication uses HTTP Basic Auth for simple access. [![Artifact Hub :zymtrace backend](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/zymtrace)](https://artifacthub.io/packages/helm/zymtrace/backend?modal=values) ```yaml auth: type: "basic" basic: enabled: true # Must be explicitly enabled username: "api-user" password: "your-secure-password" ``` :::info Limited Use Basic authentication is deprecated and will be removed in a future release. Use `local` or `oidc` for user authentication. Basic auth is disabled by default and must be explicitly enabled. ::: --- # Local Authentication URL: https://docs.zymtrace.com/authentication/local # Local Authentication Local authentication uses Zymtrace's built-in identity service to manage authentication and authorization. This is ideal for self-hosted deployments where you don't have an existing identity provider. ## Configuration Add the following to your `custom-values.yaml`: [![Artifact Hub: Zymtrace backend](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/zymtrace)](https://artifacthub.io/packages/helm/zymtrace/backend?modal=values) :::info Helm Chart Source The Helm chart source code is available on GitHub: [zystem-io/zymtrace-charts](https://github.com/zystem-io/zymtrace-charts/tree/main/charts/) ::: ```yaml auth: type: "local" admin: email: admin@company.com password: "your-secure-password" roles: - "System Admin" ``` Refer to the [Generating Signing Keys](#generating-signing-keys) section below to create a pair of keys for your production deployment. ## Generating Signing Keys When deploying to production environments, update token signing keys. Generate them using OpenSSL: ```bash # Generate ED25519 keys openssl genpkey -algorithm ED25519 -out /tmp/private.pem openssl pkey -in /tmp/private.pem -pubout -out /tmp/public.pem # View the keys cat /tmp/private.pem cat /tmp/public.pem ``` Then add the keys to your `custom-values.yaml`:
custom-values.yaml ```yaml auth: validation: # Optional: specify token issuers (defaults to gateway host) issuers: - "https://zymtrace.company.com" # Optional: specify token audiences (defaults to "zymtrace") audiences: - "zymtrace" # Token signing keys — choose one option below keys: # Option A: pass keys inline privateKey: | -----BEGIN PRIVATE KEY----- -----END PRIVATE KEY----- publicKey: | -----BEGIN PUBLIC KEY----- -----END PUBLIC KEY----- # Option B: reference an existing Kubernetes Secret # privateKeySecretName: "zymtrace-signing-keys" # privateKeySecretKey: "private-key" # publicKeySecretName: "zymtrace-signing-keys" # publicKeySecretKey: "public-key" ```
:::danger Keep Keys Secret Keep private keys secret. Never pass them inline in values files committed to version control — use Option B (an existing Kubernetes Secret) instead. ::: ## Deployment Deploy your backend with the local authentication configuration: ```bash helm upgrade --install backend zymtrace/backend \ --namespace zymtrace \ -f custom-values.yaml \ --debug \ --atomic ``` ## Accessing the System 1. Navigate to your Zymtrace URL: `https://zymtrace.company.com` 2. Log in with the admin credentials you configured 3. Create additional users Once logged in, you can access your profile and account settings: ![Profile page](/img/auth/profile-page.png) :::tip Advanced Configuration For cookie settings and token validation options that apply to both local and OIDC authentication, see the [advanced configuration guide](advanced-config). ::: --- # OIDC Authentication URL: https://docs.zymtrace.com/authentication/oidc # OIDC Authentication ```mdx-code-block import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; ``` OIDC authentication integrates with external identity providers like Google, Okta, Azure AD, or any OpenID Connect-compatible service. ## Configuration Add the following to your `custom-values.yaml`. [![Artifact Hub: Zymtrace backend](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/zymtrace)](https://artifacthub.io/packages/helm/zymtrace/backend?modal=values)
custom-values.yaml ```yaml auth: type: "oidc" # Admin user (required) admin: email: admin@company.com password: "your-secure-password" roles: - "System Admin" # OIDC provider configuration oidc: provider: # REQUIRED: OAuth2 client credentials from your OIDC provider clientId: "your-client-id" clientSecret: "your-client-secret" # REQUIRED: Your OIDC provider's issuer URI # Examples: # Google: https://accounts.google.com # Okta: https://dev-123456.okta.com # Azure AD: https://login.microsoftonline.com/{tenant-id}/v2.0 issuerUri: "https://accounts.google.com" # REQUIRED: Redirect URI after authentication # Must match what's registered in your OIDC provider # Auto-derived from ingress host if not specified redirectUri: "https://company.example.com/api/v1/oauth2/callback" ```
:::note Issuer Configuration The `issuers` field must match your actual deployment URL. If you're deploying to `https://company.example.com`, use that exact URL. The issuer is used to validate JWT tokens and must match the domain where your Zymtrace gateway is accessible. ::: ## Setting Up OIDC Providers ### Google Cloud Console Setup 1. Go to [Google Cloud Console](https://console.cloud.google.com/) 2. Create a new project or select an existing one 3. Navigate to **APIs & Services** → **Credentials** 4. Click **Create Credentials** → **OAuth 2.0 Client ID** 5. Select **Web application** 6. Add authorized redirect URI: `https://company.example.com/api/v1/oauth2/callback` 7. Copy the **Client ID** and **Client Secret** ### Configuration for Google
custom-values.yaml ```yaml auth: type: "oidc" oidc: provider: clientId: "YOUR-CLIENT-ID.apps.googleusercontent.com" clientSecret: "GOCSPX-YOUR-CLIENT-SECRET" issuerUri: "https://accounts.google.com" redirectUri: "https://company.example.com/api/v1/oauth2/callback" scopes: - openid - email - profile extraParams: - prompt=consent - access_type=offline ```
### Okta Setup 1. Log in to your [Okta Admin Console](https://developer.okta.com/) 2. Navigate to **Applications** → **Applications** 3. Click **Create App Integration** 4. Select **OIDC - OpenID Connect** and **Web Application** 5. Add sign-in redirect URI: `https://company.example.com/api/v1/oauth2/callback` 6. Copy the **Client ID** and **Client Secret** ### Configuration for Okta
custom-values.yaml ```yaml auth: type: "oidc" oidc: provider: clientId: "YOUR-OKTA-CLIENT-ID" clientSecret: "YOUR-OKTA-CLIENT-SECRET" issuerUri: "https://dev-YOUR-OKTA-DOMAIN.okta.com" redirectUri: "https://company.example.com/api/v1/oauth2/callback" scopes: - openid - email - profile ```
### Azure AD Setup 1. Go to [Azure Portal](https://portal.azure.com/) 2. Navigate to **Azure Active Directory** → **App registrations** 3. Click **New registration** 4. Add redirect URI: `https://company.example.com/api/v1/oauth2/callback` 5. Go to **Certificates & secrets** → Create a new client secret 6. Copy the **Application (client) ID** and **Client Secret** ### Configuration for Azure AD
custom-values.yaml ```yaml auth: type: "oidc" oidc: provider: clientId: "YOUR-APPLICATION-CLIENT-ID" clientSecret: "YOUR-CLIENT-SECRET" issuerUri: "https://login.microsoftonline.com/YOUR-TENANT-ID/v2.0" redirectUri: "https://company.example.com/api/v1/oauth2/callback" scopes: - openid - email - profile ```
## Deployment Deploy your backend with the OIDC configuration: ```bash helm upgrade --install backend zymtrace/backend \ --namespace zymtrace \ -f custom-values.yaml \ --debug \ --atomic ``` ## Accessing the System 1. Navigate to your Zymtrace URL: `https://zymtrace.company.com` 2. Click the OIDC login button 3. Authenticate through your OIDC provider 4. You'll be redirected back to Zymtrace Once authenticated, you can access your profile and account settings: ![Profile page](/img/auth/profile-page.png) :::tip Advanced Configuration For cookie settings and token validation options, see the [advanced configuration guide](advanced-config). ::: --- # Quick Start URL: https://docs.zymtrace.com/authentication/overview # Authentication Configuration This guide explains how to configure both user and agent authentication in Zymtrace. Authentication protects profiling data and restricts access to authorized users. ## Quick Start Use this minimal `auth` section in your `custom-values.yaml` to enable local authentication. All other fields will fall back to the defaults defined in `values.yaml`. [![Artifact Hub: Zymtrace backend](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/zymtrace)](https://artifacthub.io/packages/helm/zymtrace/backend?modal=values) :::info Helm Chart Source The Helm chart source code is available on GitHub: [zystem-io/zymtrace-charts](https://github.com/zystem-io/zymtrace-charts/tree/main/charts/) :::
custom-values.yaml ```yaml auth: # Enable built-in local authentication type: "local" # Admin user for first login admin: email: admin@example.com password: "your-secure-password" roles: - "System Admin" # Optional: enable service tokens for agents/CLI serviceToken: enabled: true ```
Refer to the [local authentication guide](local#generating-signing-keys) to create a pair of keys when deploying to production. ## Authentication Methods Zymtrace supports several authentication methods: | Auth Type | Description | Use Case | |-----------------------|------------------------------------------------|----------------------------------------| | `none` | No authentication required | Development, testing, trusted networks | | [`local`](local) | Built-in authentication with username/password | Self-hosted deployments, on-premises | | [`oidc`](oidc) | OpenID Connect integration | Enterprise SSO, Google, Okta, Azure AD | | [`basic`](basic-auth) | HTTP Basic authentication | Simple API access, service-to-service | :::tip Production Recommendation For production environments, we recommend using `oidc` for enterprise SSO integration or `local` for built-in authentication. Always use HTTPS/TLS when authentication is enabled. ::: ## Common Configuration ### Admin User Configuration Regardless of authentication type, an admin user is created automatically and has full access. ```yaml auth: admin: email: admin@example.com password: admin roles: - "System Admin" ``` :::warning Security Change default admin credentials immediately after deployment. ::: ### Service Token Configuration [Service tokens](service-tokens) enable programmatic access for agents, CLI tools, and automation. ```yaml auth: serviceToken: enabled: true # Enable service token generation and authentication ``` :::info When to Enable Enable when: - Using CI/CD pipelines - Running agents outside trusted networks - Building custom API integrations ::: :::warning Security Running the agent with `-disable-tls` exposes the token publicly. ::: --- # Role-Based Access Control URL: https://docs.zymtrace.com/authentication/rbac # Role-Based Access Control (RBAC) Zymtrace uses Role-Based Access Control (RBAC) to manage user permissions. Roles are assigned to users, and each role contains a set of permission patterns that define what actions the user can perform. :::tip Quick Start Assign built-in roles to users via **Profile picture** → **Account** → **Users** → select user → **Edit** → add roles → **Save** ::: ## How RBAC Works 1. **Roles** are assigned to one or more **users** 2. **Roles** contain **permission patterns** (not individual permissions) 3. **Permission patterns** use wildcards to match multiple permissions 4. **Services** validate requests by matching required permissions against patterns in the token ## Permission Patterns Permission patterns use a hierarchical format with wildcard support: ``` resource:subresource:action ``` **Wildcard Types:** - `*` - matches a single level (e.g., `project:*` matches `project:create`, `project:read`) - `**` - matches multiple levels (e.g., `**:read` matches `project:read`, `admin:user:read`, etc.) **Examples:** ```bash # Allow all project operations project:* # Allow reading anything **:read # Allow all admin operations admin:** # Allow all service token operations service:token:* # Allow everything (admin role) **:** ``` ## Built-in Roles Zymtrace includes three default roles: - `System Admin` (full system access with all permissions) - `System Editor` (can edit all system resources) - `System Viewer` (read-only access to all system resources) - `Default Project Viewer` (all users have read-only access to the default project) ## Permission Model Understanding available permissions is essential for creating custom roles. ### Service Token Permissions | Description | Permission | |-----------------------|------------------------| | Create service tokens | `service:token:create` | | View service tokens | `service:token:read` | | Revoke service tokens | `service:token:revoke` | ### Project Permissions | Description | Permission | |-------------------------|------------------| | Create new projects | `project:create` | | View project details | `project:read` | | Modify project settings | `project:update` | | Delete projects | `project:delete` | ### Admin User Permissions | Description | Permission | |----------------------|---------------------| | Create user accounts | `admin:user:create` | | View user accounts | `admin:user:read` | | Modify user accounts | `admin:user:update` | | Delete user accounts | `admin:user:delete` | ### Admin Role Permissions | Description | Permission | |-------------------------|-----------------------------| | Create custom roles | `admin:role:create` | | View role definitions | `admin:role:read` | | Modify role permissions | `admin:role:update` | | Delete custom roles | `admin:role:delete` | | Assign roles to users | `admin:role:assignment:set` | ### Audit Permissions | Description | Permission | |------------------------|--------------------| | View audit log entries | `admin:audit:read` | ### Authentication Permissions | Description | Permission | |-------------------------------|--------------------| | Log in with local credentials | `auth:local:login` | ## Common RBAC Patterns Here are practical role configurations for common use cases: ### Read-Only Viewer Perfect for stakeholders who need access but shouldn't make changes. ``` **:read ``` ### CI/CD Agent Role For automated deployments and monitoring systems. ``` service:token:read project:read ``` ### Project Manager Can view and modify project settings, but cannot manage users or roles. ``` project:read project:update ``` ### Analyst Role Can create and view resources but cannot delete. ``` project:create project:read admin:audit:read ``` User roles can be managed in the admin page. ## Managing User Roles 1. Log in as an admin user 2. Navigate to **Profile picture** → **Account** → **Users** ![Users list](/img/auth/users-list.png) 3. Select a user and click three vertical dots 4. Click **Edit** ![Edit user dialogue](/img/auth/edit-user-dialogue.png) 5. Add or remove roles 6. Save changes :::tip Changes Don't Immediately Take Effect Role changes apply when the user's next access token is issued. Access tokens are short-lived (typically 5 minutes), so changes take effect quickly. ::: ## Creating Custom Roles Custom roles allow fine-grained access control tailored to your organization's needs. 1. Navigate to **Profile picture** → **Account** → **Users** 2. Click **Role & Permissions** 3. Click **Add Role** ![Create new role dialogue](/img/auth/create-new-role-dialogue.png) 4. Add permission patterns and role name 5. Save the role ## Roles with Constraints Roles can be restricted to specific projects, enabling fine-grained project-level access control. ### Project-Scoped Roles When creating or editing a role, you can specify a list of projects the role grants access to: 1. Navigate to **Profile picture** → **Account** → **Users** 2. Click **Role & Permissions** 3. Click **Add Role** or edit an existing role 4. Add permission patterns 5. Under **Project Access**, select which projects this role applies to 6. Save the role ### Example: Project Lead A project-scoped role that allows users to manage a specific project: ``` project:read project:update ``` Apply this role with project constraint to **"Project A"** to allow leadership to modify only that project's settings. ## Audit Logging All permission-related actions are logged to the audit log: * Role assignments and removals * Role creation, modification, and deletion * Permission checks that result in denial * Service token creation and revocation * Admin actions on user accounts --- # Service Tokens (Profiler & CLI) URL: https://docs.zymtrace.com/authentication/service-tokens # Service Tokens (Profiler & CLI) Service tokens enable authenticated access for the [Zymtrace CLI](../symbols/upload-symbols.md), [Profiling Agent](../install/profiler/install-profiler.md), [MCP](../mcp/mcp-token.md), as well as programmatic API access to Zymtrace. When authentication is enabled (`local` or `oidc`) along with service token authentication, both the Zymtrace profiler and Zymtrace CLI can be authenticated using a service token. :::info Prerequisites Ensure that `auth.serviceToken.enabled` is set to `true` in your Helm values. This enables the service token functionality required for Zymtrace profiler and Zymtrace CLI authentication. See the [overview page](overview#service-token-configuration) for configuration details. ::: ## Getting a Service Token 1. Log in to Zymtrace as an admin or user with `service:token:create` permission 2. Navigate to **Your profile picture** → **Account** → **Service Tokens** ![Service Token list](/img/auth/service-tokens-list.png) 3. Click **Create Token** ![Create service token dialogue](/img/auth/create-service-token-dialogue.png) 4. Provide a descriptive name for the token (e.g., "Production Profiler - Server01" or "CI/CD CLI Access"), select when the token should expire and click 'Create Token' 5. Copy the token and store it securely (it won't be shown again) 6. Save the token in a secure location ## Using Tokens with Zymtrace Profiler Pass the token to the profiler using the `-auth-token` flag: ```bash # Using CLI flag sudo ./zymtrace-profiler \ -collection-agent=your-gateway-host.com:443 \ -auth-token="your-service-token-here" ``` You can also set the token via the `ZYMTRACE_AUTH_TOKEN` environment variable: ```bash export ZYMTRACE_AUTH_TOKEN="your-service-token-here" sudo ./zymtrace-profiler \ -collection-agent=your-gateway-host.com:443 ``` ### Helm (Kubernetes) {#profiler-token-helm} For Helm deployments, source the token from a Kubernetes Secret with `profiler.authToken` instead of passing it as a CLI arg. The chart exposes it to the agent as `ZYMTRACE_AUTH_TOKEN`, keeping it out of the rendered pod spec. Let the chart create the Secret from a plain value: ```bash helm install profiler zymtrace/profiler \ --namespace zymtrace \ --set profiler.authToken.value="your-service-token-here" ``` Or reference an existing Secret you manage (takes precedence over `.value`; `key` defaults to `auth-token`): ```bash kubectl create secret generic my-zymtrace-token \ --namespace zymtrace \ --from-literal=auth-token="your-service-token-here" helm install profiler zymtrace/profiler \ --namespace zymtrace \ --set profiler.authToken.existingSecret.name=my-zymtrace-token \ --set profiler.authToken.existingSecret.key=auth-token ``` See the [profiler install guide](../install/profiler/install-profiler.md#helm-auth-token) for details. :::warning Token Security - Never commit service tokens to version control - Rotate tokens periodically - Use separate tokens for different environments (dev, staging, production) - Revoke tokens if they are compromised ::: For AI assistant integration via MCP, see the [MCP documentation](../mcp/) page. ## Token Management ### Viewing Tokens View all service tokens in Zymtrace settings page: 1. Navigate to **Your profile picture** → **Account** → **Service Tokens** 2. See token name, type, creation date, and expiration ### Revoking Tokens To revoke a service token: 1. Navigate to **Your profile picture** → **Account** → **Service Tokens** 2. Find the token you want to revoke 3. Click the three dots menu 4. Select **Revoke** 5. Confirm the revocation :::warning Immediate Effect Revoking a token takes effect immediately. Any Zymtrace profiler instances or CLI tools using the token will lose access. ::: --- # Troubleshooting URL: https://docs.zymtrace.com/authentication/troubleshooting # Troubleshooting Authentication This guide covers common authentication issues and their solutions. ## Authentication Not Working **Symptom**: Users cannot log in or agents are rejected 1. Check that auth type is correctly set: ```bash kubectl get configmap -n zymtrace zymtrace-auth-config -o yaml | grep "AUTH" ``` 2. Verify signing keys are configured: ```bash kubectl get configmap -n zymtrace zymtrace-auth-config -o yaml | grep "PRIVATE_KEY" ``` 3. Check service logs: ```bash # Identity service logs kubectl logs -n zymtrace -l app=identity --tail=100 # Gateway logs kubectl logs -n zymtrace -l app=gateway --tail=100 ``` ## OIDC Redirect Issues **Symptom**: Authentication fails after redirect from OIDC provider 1. Verify redirect URI matches exactly what's registered: ```yaml # In your OIDC provider console Redirect URI: https://company.example.com/api/v1/oauth2/callback # In your values.yaml redirectUri: "https://company.example.com/api/v1/oauth2/callback" ``` 2. Check for trailing slashes or protocol mismatches (http vs https) 3. Ensure `issuerUri` matches your OIDC provider exactly ## Token Validation Failures **Symptom**: Tokens are rejected even when valid 1. Verify issuer and audience configuration: ```bash kubectl get configmap -n zymtrace zymtrace-config -o yaml | grep "ISSUER" kubectl get configmap -n zymtrace zymtrace-config -o yaml | grep "AUDIENCE" ``` 2. Check that public key matches private key: ```bash # Test key pair (outside cluster) echo "test" | openssl dgst -sha256 -sign private.pem | \ openssl dgst -sha256 -verify public.pem -signature - ``` 3. Ensure gateway URL is accessible and matches issuer ## Cookie Issues **Symptom**: Users logged out frequently or unable to stay logged in 1. Verify cookie settings match your deployment: ```yaml # For HTTPS deployments secure: true # For HTTP deployments (development only) secure: false ``` 2. Check browser console for cookie warnings 3. Verify SameSite settings for cross-domain scenarios ## Service Token Issues **Symptom**: Agents or CLI tools cannot authenticate with service tokens 1. Verify service tokens are enabled: ```bash kubectl get configmap -n zymtrace zymtrace-auth-config -o yaml | grep "SERVICE_TOKEN" ``` 2. Check that the token hasn't expired 3. Verify the token is being passed correctly to the agent: ```bash # Check agent logs for authentication errors kubectl logs -n zymtrace -l app=agent --tail=50 ``` 4. Ensure the token has the correct permissions (see [RBAC guide](rbac)) ## Permission Denied Errors **Symptom**: Users receive "permission denied" errors when accessing features 1. Check user's assigned roles: - Navigate to **Profile picture** → **Account** → **Users** - Verify the user has appropriate roles 2. Verify role permissions: - Navigate to **Role & Permissions** - Check that the role has the required permission patterns ## Deployment Validation Use these commands to validate your authentication deployment: ```bash # Check all auth-related environment variables kubectl get configmap -n zymtrace zymtrace-auth-config -o yaml | grep -E "(AUTH|OIDC|IDENTITY|WEB__AUTH)" # Test gateway connectivity kubectl port-forward svc/zymtrace-gateway 8080:80 -n zymtrace curl -v http://localhost:8080/ # Check identity service status kubectl get pods -n zymtrace -l app=identity kubectl logs -n zymtrace -l app=identity --tail=50 ``` ## Common Error Messages ### "Invalid token" **Cause**: Token has expired, is malformed, or signing keys don't match **Solution**: - For users: Log out and log back in - For agents: Generate a new service token - Verify signing keys are configured correctly ### "Insufficient permissions" **Cause**: User's roles don't include required permissions **Solution**: - Assign appropriate roles to the user - Create a custom role with required permissions - See [RBAC guide](rbac) for permission patterns ### "OIDC provider unreachable" **Cause**: Cannot connect to OIDC provider's issuer URI **Solution**: - Verify `issuerUri` is correct and accessible - Check network connectivity from the cluster - Verify firewall rules allow outbound HTTPS ### "Redirect URI mismatch" **Cause**: The redirect URI in your config doesn't match what's registered in the OIDC provider **Solution**: - Ensure both URIs match exactly (including protocol and trailing slashes) - Update OIDC provider configuration if needed - Redeploy with corrected configuration ## Getting Help If you continue to experience issues: 1. Collect logs from identity and gateway services 2Contact support at [support@zymtrace.com](mailto:support@zymtrace.com) 3Include your authentication configuration (without secrets) --- # Changelog URL: https://docs.zymtrace.com/changelog # zymtrace Changelog ## v26.10.2 ### Tweaks - cudaprofiler: Improve the host-side overhead of CUDA profiling - CUDA graph launches are sampled separately via the new `ZYMTRACE_CUDAPROFILER__GRAPH_SAMPLING__*` settings (default target 20); a sampled graph launch profiles every kernel in the graph - Per-kernel CUPTI callbacks are only enabled while a sampled launch is in flight - The sampler tuning also applies to ROCm profiling; the `ZYMTRACE_ROCMPROFILER__*` defaults are unchanged - Lower default sampling targets per OS thread - profiler: improve native unwinding overhead by caching mapping and stack delta lookups in eBPF (about 128 KiB per core) - web: fix overlapping GPU timeline samples on the same stream nesting under the wrong parent instead of branching at their shared stack prefix - ui: fix pages of new projects not showing data until reloaded - ui: fix a crash when clicking a MIG device in the MIG dashboard ## v26.10.1 ### Major - profiler: add PC sampling for AMD GPUs, showing where time is spent inside ROCm kernels - profiler: support Python 3.15 - web: application recommendations now fully replace the previous per-trace recommendations - Recommendations are now kept up to date by background workers - The "Seed now" button is gone; seeding happens automatically - Upgrading deletes the stored per-trace recommendations and their suppressions - all: record each host's type, such as its cloud instance type, and allow filtering by `host.type` - all: estimate the annual CPU cost of traces, functions, top entities and recommendations - Priced per instance type in Settings → Cost Estimates - Beta: gated behind the "Costs" feature flag - all: add K8s Capacity IQ - Compares Kubernetes CPU and memory requests and limits against the containers' observed usage - Suggests better values to apply to reduce resource waste and save costs - Beta: gated behind the "K8s Capacity IQ" feature flag ### Tweaks - profiler: support Linux 7.1 kernels - backend: Slack notifications for XID errors link to the affected GPU - backend: accept integer literals in agent config rule expressions (e.g. SLURM ID) - cudaprofiler: update to CUPTI 13.4 Update 1, which fixes CPU usage that kept growing during long-running activity tracing - While active, CUPTI 13.4 blocks live migration of vGPUs - helm: support disabling automatic TTL management and allow control of `materialize-ttl-after-modify` - profiler: fix AWS and Azure hosts not reporting their cloud metadata, such as the instance type - Reading EC2 instance tags needs the `ec2:DescribeTags` permission; without it, the profiler logs one warning per start - profiler: fix an issue where the profiler would ignore trailing command line options - profiler: fix rare cases where a silently broken backend connection could stall the profiler's exports for several minutes and raise its memory usage - ui: bring back force-scheduling recommendations, with a confirmation dialog - ui: metric graphs and their tooltips list the largest series first - ui: fix switching percentiles in percentile metric graphs - ui: fix filtering by Slurm job ID in basic query mode - ui: allow filtering agent config rules by Slurm job ID and user - ui: add expired status filter to service tokens page - ui: fix an issue where pressing Cmd+K in the API Explorer opened the app command menu instead of the API Explorer search - ui: fix an issue where the update button in the provider and MCP server dialogs could get stuck in disabled state - ui: fix text inputs only reacting to clicks on part of the field - ui: browser tabs now show the name of the current page - all: various smaller tweaks and reliability improvements ## v26.10.0 ### Tweaks - backend: an application running on many clusters or hosts now gets one recommendation, not one per subset - backend: upgrading clears existing application recommendations - They refill on their own or via "Seed now" - ui: application recommendations switch to all time when the selected range has none - ui: application recommendations can be listed 10, 20, 50 or 100 per page - ui: application recommendations "All" tab previews the top entries of each event kind - all: various smaller tweaks and reliability improvements ## v26.9.5 ### Tweaks - profiler: add -insecure flag to use TLS but skip certificate validation ## v26.9.4 ### Major - web: add an MCP endpoint for timeline patterns ### Tweaks - all: the global filter bar now also filters application recommendations - backend: application recommendation prompts include derived thread, subtree and function tables by default - backend: application recommendations now request savings estimates bounded by measured work - backend: the "Seed now" button seeds each application from its heaviest recommended trace still running - mcp: the `recommendations` MCP tool returns application recommendations when that mode is enabled - profiler: improve reliability of Java StubRoutines frame naming for OpenJDK - profiler: tolerate stale `eh_frame_hdr` and fall back to walking `eh_frame` in that case - profiler: fully support changing on-CPU sampling rate on the fly - ui: restore error frames in flamegraph exports from the support dialog - ui: switching projects on an application recommendation no longer shows "resource not found" - ui: the application recommendations list keeps its time scope when returning from a recommendation - all: various smaller tweaks and reliability improvements ## v26.9.3 ### Major - all: add new application scoped recommendation mode (beta) ### Tweaks - ui: truncate long frame names on flamegraph hover tooltip - ui: adjusted agent config rule editor to only write explicitly set settings - ui: fix a crash on the recommendations page in Firefox when switching to the CPU or CUDA tab - ui: add ability to enable high-res profiling directly from the CUDA timeline page - ui: fix alt-shading on the timeline - ui: add a command palette for quick navigation to pages and actions (Ctrl+K) - profiler: fix Node.js profiles showing no JavaScript frames on Linux 6.11 and newer in some cases - profiler: fix issues when running profiler with OTEL protocol in some configurations - profiler: allow overriding samples per second from the agent config at runtime - backend: improve handling of functions that appear on several lines in hot traces and recommendations - backend: use configured display options consistently, including for raw API requests - all: various smaller tweaks and reliability improvements ## v26.9.2 ### Major - web: ROCm profiling views and dashboards are now enabled by default - web: feature flags are now stored in the backend and managed from Settings → Feature Flags ### Tweaks - all: migrate the bundled object storage from MinIO to Silo - drop-in replacement, existing volumes are taken over in place - no action required unless you mirror images or override the storage image - if you do, switch to `pgsty/silo:RELEASE.2026-09-03T13-18-01Z` - cudaprofiler: support cuda 13.4 - ui: keep the session on transient token refresh failures instead of logging out - ui: fix stack frame references matching shorter frame names - ui: add differential functions view for allocation profiles - backend: send slack notification (if configured) on XID errors on CUDA hosts - backend: bump bundled ClickHouse to 25.3.14.14 - backend: fix hot traces API and MCP endpoints not applying correct time filters - profiler: support off-CPU profiling on Linux 7.0 - profiler: upgrade profiler to Go 1.27 and support symbolizing / unwinding it - all: various smaller tweaks and reliability improvements ## v26.9.1 ### Tweaks - ingest: reduce active agent counting contention - cudaprofiler: fix a crash on process exit when CUDA work is flushed after thread-local state was torn down (seen with PyTorch) ## v26.9.0 ### Major - ui: add standalone assistant page ### Tweaks - all: add grace period on license expiry during which profiles are still ingested - cudaprofiler: attribute `cudaSynchronizeEvent` calls to a stream - profiler: support Zing versions from 26.04 to 26.07 - profiler: demangle auto-generated rocblas kernel names - profiler: sanitize GPU-related autogenerated paths - ui: timeline shows separate lane for sync events, since they overlap with kernels - ui: add separate set of settings for collapsed sync lanes in timeline - ui: timeline sidebar is now resizable - all: various smaller tweaks and reliability improvements ## v26.8.5 ### Major - all: add AMD GPU profiling and metrics - backend: add slack integration for recommendations ### Tweaks - all: add tiered retention support for CH - backend: cache more query results to improve request latency - cli: add commands to manage user identities and roles - ui: add a memory lane to timeline - ui: add aggregation settings to timeline - ui: allow enabling high-resolution profiling from an existing rule's edit dialog - ui: block saving GPU profiler overrides until automatic CUDA profiling is enabled - ui: highlight potentially small leafs in the timeline - ui: make collapsed timeline view more customizable - ui: show GPU time and GPU share for pods, deployments, containers, and executables on the dashboard and top entities pages - ui: show the project selection screen instead of switching you to another project when you cannot access the requested one - ui: zoom into the timeline on load if too many nodes are drawn - all: various smaller tweaks and reliability improvements ## v26.8.4 ### Tweaks - ui: sort CPU recommendations by occupied cores consistently across the recommendations page, previews, dashboard insights, and the profile sidebar - ui: list recommendations in flyouts across all time instead of only the selected range, so older recommendations stay visible - ui: show recommendation graphs with surrounding time context and highlight the detected region - ui: add support for dismissing and restoring recommendations - all: various smaller tweaks and reliability improvements ## v26.8.3 ### Tweaks - backend: cache exact SymDB lookups, taking ~50 ms off hot flamegraph queries - backend: add aggregation settings to diff native functions across executables - profiler: upscale off-CPU samples so that samples from different but same-length time ranges with different sampling rates are comparable - profiler: reduce eBPF map memory usage substantially (~60–70 MiB per agent) using new kernel features - profiler: reduce CPU use by using `PROCMAP_QUERY` instead of parsing `/proc/$PID/maps` on kernels that support it - profiler: further reduce CPU usage of the Zing unwinder - profiler: improve unwinding reliability of Go frames that call into the vDSO - ui: improve rendering of dense timeline regions - ui: add help overlay for controls on timeline view - ui: high-resolution profiling is now saved as its own short-lived agent config rule with a duration, separate from other settings and their expiry - ui: diff pages now keep aggregation settings in the URL so shared links preserve them - all: various smaller tweaks and reliability improvements ## v26.8.2 ### Major - ui: add GPU infrastructure view ### Tweaks - profiler: don't warn about a missing CUDA implant on machines without a GPU - ui: fix display options divergence banner re-appearing after discarding URL settings - web: fix the MCP endpoint rejecting remote clients with `403 Forbidden: Host header is not allowed` - Accepted hosts are configurable via `rest.mcp-allowed-hosts` or Helm equivalent - web: speed up dashboard GPU top functions computation when large amounts of GPU events are present - all: various smaller tweaks and reliability improvements ## v26.8.1 ### Major - mcp: significantly improve MCP - Add new tools to discover the environment and find optimization opportunities - Reworked many existing tools to make more intuitive to use for LLMs ### Tweaks - profiler: reduce CPU usage by ~15% - profiler: reduce memory usage by ~20% - profiler: improve Python unwinder reliability in some edge cases - ui: reorganize navigation entries and move profile pages into separate views - ui: stream the GPU timeline in chunks so it renders while events are still loading - all: various smaller tweaks and reliability improvements ## v26.8.0 ### Tweaks - profiler: add `--json-log` option to output logs as json - profiler: rate limit log messages per call site - web: improve recommendation query performance in distributed deployments - all: various smaller tweaks and reliability improvements ## v26.7.3 ### Tweaks - backend: backend services now expose their metrics on health ports (Prometheus format) - profiler: improve Python unwinding when `kernel.numa_balancing` is active - ui: add recommendations sidebar to CPU and GPU profiles - ui: add off-CPU tab to the diff top functions view - ui: timeline density chart and its summary now show GPU time instead of event count - ui: fix top entities page showing stale data after switching projects with an absolute time range - ui: fix login form clipping on short viewports - ui: fix create agent rule always opening the CUDA profiler editor instead of the selected rule type - all: various smaller tweaks and reliability improvements ## v26.7.2 ### Major - profiler: add off-cpu profiling and scheduler latency - profiler: add high resolution profiling and timeline view ### Tweaks - all: add off-CPU rate to agent config - all: add Zing offsets to `AgentConfig` - all: support Zing 26.02 - backend: significantly improve performance of profile lookups in large deployments - profiler: enable GPU metrics by default - profiler: reduce Kubernetes API server and etcd load by serving pod metadata lookups from cache - profiler: support automatic injection of CUDA profiler on `arm64` - ui: fix preset providers not appearing in recommendations settings after adding them on the providers tab - ui: fix provider preset detection in provider list - ui: greatly improve navigation, sorting, filtering speed on the diff top functions page - ui: keep diff filters intact when redirected back to a page after login - ui: reorganize sidebar navigation with denser layout - ui: show all-time recommendations on the dashboard recommendation card when the selected time range has none - all: various smaller tweaks and reliability improvements ## v26.7.1 ### Tweaks - ui: fix agent config query bar focus handling - all: various smaller tweaks and reliability improvements ## v26.7.0 ### Major - ui: promote CUDA profiler injection and GPU profiler config settings from experimental to beta - ui: promote agent configuration from beta to general availability ### Tweaks - profiler: support JDK26 unwinding - cudaprofiler: work around CUPTI reporting invalid timestamps - ui: add CSV export in agent inventory view - ui: expose load ratio in the recommendations config - ui: add stall reason description to flamegraph flyout - ui: split agent configuration into categorized tabs - ui: add JSON export for flamegraphs and events on profile views - ui: add K8S pod label filter to recommendations page - backend: attach read perms to newly created CLI access tokens - backend: improve Python internals web filter, to reduce noise in flamegraph - backend: document OpenAPI query field in the web event filter - backend: add recommendation force-scheduling endpoint - backend: add ability to change recommendation status through API - backend: fix an OpenAPI name clash for event/flamegraph JSON frames - all: various smaller tweaks and reliability improvements ## v26.6.1 ## Major - all: the recommendation engine is now in public beta - Simply head to the new "Recommendations" tab in the UI and follow the instructions to set it up - profiler: add experimental support for injecting the GPU profiler into CUDA processes - This is an alternative to setting `CUDA_INJECTION64_PATH`, in cases where environment variables can't be modified - The processes into which to inject are configured via the agent config settings in the UI ## Tweaks - ui: stop auto-scrolling the AI assistant chat during responses and add a scroll-to-bottom indicator - ui: add trace tag filters in the recommendations view - ui: add performance trend plots for recommendations - ui: expose load ratio in the recommendations config - web: fix bug where only one top function was shown in frame details page - profiler: collect more GPU metrics - all: various smaller tweaks and reliability improvements ## v26.6.0 ### Major - all: support attaching arbitrary Kubernetes pod labels to profiles - Profiler - Users can now add `-k8s-pod-labels` flag to the profiler CLI - Comma-separated list of Kubernetes pod labels to extract the values of and attach to host metadata - Note: values should be reasonably low cardinality - UI - New in-context filter options are now available on most pages to filter by these pod labels - all: the recommendation feature is now available as private beta - If you would like to participate, please contact us! - mcp: integrate all major OpenAPI endpoints into MCP ### Tweaks - profiler: support more exotic Python builds with PGO but stripped `.cold` symbols - For example, this allows unwinding for Python 3.13 from deadsnakes PPA - ui: break down inference metrics by consumer - ui: display total count in agent inventory - web: improve query performance on GPU pages (and also in general) - all: various smaller tweaks and reliability improvements ## v26.5.0 ### Major - ui: add agent inventory view - Shows information and version of all profilers that are currently reporting into zymtrace - Accessed via `Settings` → `Profiler Inventory` ### Tweaks - profiler: fix symbolization for inlined frames - profiler: fix an issue that could cause missing Python frames for CUDA processes - ui: fix an issue that could cause the time range to be reset after login redirect - web: improve vLLM + SGlang metrics readability by switching to rate aggregation - all: various smaller tweaks and reliability improvements ## v26.4.5 ### Major - profiler: add support for Zing versions >25.02 - ui: rework query filter handling - add grouping of filter fields and add search support in basic query mode - support using global filter queries on metric pages - add in-context filters to selected pages ### Tweaks - assistant: add stop button to chat window - assistant: don't set temperature for OpenAI-compatible providers - backend: reduce noise in logging output of all backend services - cudaprofiler: add experimental option to use hardware event system on Blackwell+ - enable via `ZYMTRACE_CUDAPROFILER__INTERNAL__TRY_ENABLE_HES=true` - cudaprofiler: correctly associate GPU events to the exact CPU thread that initiated them - profiler: improve logic to find NVML on ARM64 + NixOS - profiler: add user-defined job tag environment variable for tagging processes - ui: add display option to hide system libraries - ui: fix an issue that could cause chart tooltips to be cut off in some cases - all: many smaller tweaks and reliability improvements ## v26.4.4 ### Tweaks - cudaprofiler: fix issue preventing profiler from being loaded in some cases on ARM64 - cudaprofiler: significantly improve profiler efficiency on ARM64 systems - profiler: improve unwinding reliability for Python 3.13+ on ARM64 - ui: fix an issue that could cause charts to overflow on Safari ## v26.4.3 ### Tweaks - cudaprofiler: fail gracefully if `libcupti` or `libnvperf_host` can't be loaded - ui: fix an issue that could cause the port to not be included in the agent installation script ## v26.4.2 ### Tweaks - helm: support using existing K8S secrets for license and TLS keys ## v26.4.1 ### Major - profiler: add support for extracting SLURM jobs container metadata - requires `cgroup/v2` plugin to be enabled in `cgroup.conf` ### Tweaks - backend: fix function performance trend for frames such as `Garbage Collector` - backend: allow to filter profiles by GPU name or UUID - ui: support `!` in CEL queries - ui: order `Top Functions` by self cores by default to sync with dashboard - ui: add new `Aggregation Settings` - Option to strip parameters and return types (default: on) - Option to abbreviates package names to their first character (default: off) - ui: disable Go + JVM GC + scheduler frame grouping by default - cudaprofiler: improve accuracy + formatting of stall reasons - cudaprofiler: improve reconnect behavior if profiler restarts - cudaprofiler: reduce storage and network cost of CUDA traces - all: add support for filtering events by CPU architecture - all: various smaller tweaks and reliability improvements ## v26.4.0 ### Major - profiler: add support for `NVIDIA Dynamo-Triton` as application type for automated metrics detection and extraction - enabled (or disabled) via the `-enable-triton-metrics` flag - cudaprofiler: extract NVTX regions if available - cudaprofiler: add support for profiling memory and synchronization operations ### Tweaks - ui: add 'show root' group-by option for flamegraphs - ui: improve space utilization of chat messages inside the ai-assistant chat window - ui: allow AI assistant configuration via UI - ui: add sync toggle to metric dashboard group-by dropdown - ui: `matches()` CEL function now supports array backed columns - profiler: support versioned libruby filenames (e.g. `libruby.so.3.2.10`) - profiler: support statically linked Ruby - profiler: reduce memory usage and improve handling of kernel stacks - cudaprofiler: improve sampling accuracy - web: add stall reason aggregation - web: fix computation of cores used on diff page - all: various smaller tweaks and reliability improvements ## v26.3.2 - backend: fix MCP compatibility with latest Cursor versions - all: various smaller tweaks and reliability improvements ## v26.3.1 - assistant: fix issue where MCP sessions could stop working when used via assistant - web: further improve metrics accuracy in certain edge cases - all: various smaller tweaks and reliability improvements ## v26.3.0 - profiler: fix issue where hotspot stub frames could stop being shown after extended time - cudaprofiler: build against older glibc to enhance compatibility with older workloads - web: improve metrics accuracy under specific circumstances ## v26.2.7 - helm: support configuring the priority class of the profiler daemon set - profiler: fix bug that could cause CPU metrics CPU usage to be higher than the number of cores the machine has - profiler: improve logic for scanning for NVML + add more config options to tweak it - profiler: report working set size metric (as displayed by K8S tooling) as well - ui: fixed an issue that could cause the table header to not be shown correctly when the table is scrolled - ui: fixed GPU stats not showing up correctly on the metric pages - ui: fixed an issue that could cause the datetime input in the time range picker to show an invalid state when manually entering a date - ui: fixed an issue that could cause the top entities view to not be updated correctly when the filter query changed - ui: improve the placement of the "Apply" button in the time range picker popover - web: fixed an issue that could cause MCP sessions to break after a while ## v26.2.6 - profiler: support dynamically linked NodeJS - profiler: support Perl 5.42 - profiler: refactor kernel symbolization - slight memory use reduction - symbol information is now updated upon BPF program loads, similar to kernel modules - profiler: handle failures to place uprobes more gracefully - profiler: support Go 1.26.0 - profiler: fix bug where script name was not propagated to all GPU traces - profiler: check PCI devices to determine if NVIDIA GPUs are present - cudaprofiler: fix rare bug that could cause profiles to not be exported after a long time, even though they were captured by the implant - cudaprofiler: fix rare segfault due to an Nvidia bug in CUPTI 12/13 - cudaprofiler: update CUPTI version used for CUDA 13 workloads to 13.1 - web+profiler: allow to group flamegraph by GPU - web+profiler: add `-cluster-name` option and ability to filter by it in the UI - ui: add new metrics dashboard pages - ui: fix an issue where a custom font size could cause parts of the UI to not be rendered correctly - ui: fix an issue that could cause the last value of a series to not be shown correctly in the metrics chart - ui: fix an issue that could cause the query bar to show different percentage values between basic and advanced query modes - ui: add indicator for active event kind in query bar - ui: add project scoping for agent config rules - ui: add MIG metrics dashboard to the GPU metrics page - ui: add support for value autocomplete inside query input in agent config - ui: add a description for each of the available agent config settings - ui: fixed an issue that could cause display options to not be persisted correctly when accepting divergent options from the URL - assistant: fix MCP access from the assistant - assistant: add Gemini 3+ preview models - ui: fix an issue that could cause the aggregation type to not be shown correctly on the metric charts ## v26.2.5 - profiler: correctly ensure periodic resend of frame metadata ## v26.2.4 - profiler: retry loads of NVML to support environments where the Nvidia driver is installed after profiler startup ## v26.2.3 - ui: fix an issue that could cause the core count inside the host stats to show with decimal digits - profiler: performance and stability improvements for alloc profiling ## v26.2.2 - backend: implement new metrics schema - mcp: support for allocation profiles - profiler: fix bug in Java alloc profiling if multiple different JDKs are used simultaneously - profiler: fix duplicate frames in native unwinder on ARM64 - profiler: improve performance of zing (alloc) symbolization by ~10-20% - profiler: improve export and retry behavior when running into size limits - profiler: support JVMTI based alloc profiling for HotSpot JDK (Zulu, Amazon builds, etc) for x86 - ui: fix an issue that could cause the host stats to not be shown correctly in the host stats overview - ui: fix an issue where entity actions could potentially not include all relevant filters when they were executed - web: add endpoint to store/load assistant config ## v26.2.1 - ui: fixed an issue that could cause the wrong URL params to be applied when using the actions inside the top entities table - profiler: make errors encountered during metrics startup non-fatal - profiler: ensure periodic frame / exe data resends for when function records are TTL'ed out in ClickHouse - web: improve default NodeJS frame filters - profiler: improve behavior when frame and executable reporting fails due to network errors - retry requests and invalidate internal caches to accelerate resends ## v26.2.0 - profiler: support getting GPU utilization for MIG devices - profiler: support .NET 9.0 and 10.0 - profiler: support Node v24.11.1 and up - profiler: support Node on ARM64 - profiler: don't load network probes on unsupported systems - profiler: significantly improve Java alloc profiling for Zing - web: fix OpenAPI schema for web metrics filter - web: allow changing settings and secrets with disabled auth - assistant: limit the flamegraph data to below 200k tokens - profiler/web: support dynamic agent configuration via the web UI (beta) - add support for per host and per process settings (via CEL expressions also used for our search) - add support for updating config settings without restarting the agent - only some settings are supported as of yet, the remaining ones will be added incrementally ## v26.1.2 - assistant: add suggested prompts separated by CPU and GPU context - profiler: add support for metrics reporting for AWS Neuron (Inferentia/Trainium) devices - requires a supported EC2 instance host with one or more Neuron devices - enabled via the `-enable-neuron-metrics` flag - profiler: add (per process) network throughput metrics - these are enabled by default, and can be disabled via `-enable-network-metrics=false` - by default, they exclude local traffic (`127.0.0.1` etc), which can be included with `-track-local-traffic` - profiler: make I/O throughput metrics per process - profiler: add support for `SGLang` as application type for automated metrics detection and extraction - enabled (or disabled) via the `-enable-sglang-metrics` flag - all: replace `jemalloc` with `mimalloc` - allows deploying the backend on non-4K page machines ## v26.1.1 - web: allow kernel functions to be viewed in top functions page - web: add CEL array type support - web: add ability to group by deployment name - web: improve metrics performance using pre-filtering by `meta_id` - ui: fix an issue that could cause the sandwich graph on the top-function page to not show up correctly after re-loading the data - ui: fix handling of non-ascii chars in metric attributes - ui: fix querybar autocomplete suggestions not showing up correctly in Firefox - ui: fix an issue that caused the middle truncation of text to not work correctly in Firefox - profiler/symdb: improve handling of file size limit if `-dwarf` is passed - profiler: add allocation profiling for Java (Zing + OpenJDK) - enabled via `-alloc-profile ` flag, instructing the profiler to profile allocations every ` bytes` - for example, `-alloc-profile 8mib` - profiler: reduce likelihood of incorrect executable paths (e.g. just `/`) showing up - this could happen due to the kernel reporting incorrect paths directly after `execve` ## v26.1.0 - assistant: add support for PDF uploads - assistant: show consumed tokens at bottom of answer - assistant: add support for image uploads - profiler: fix edge cases in metrics collection - processes that were fully idle for a full collection cycle would sometimes report incorrect metrics data - systems that were launching a lot of processes might've reported incorrect metrics data - both of those bugs are fixed now - profiler: fix GPU metrics collection in the presence of more than one GPU - profiler: automatically detect and extract metrics from supported applications - currently `vLLM` is supported - enabled (or disabled) via the `-enable-vllm-metrics` flag - web: add metric activity aggregation endpoint - cudaprofiler: fix several bugs - added better handling of CUDA graphs - fix stack trace correlation when PC offsets are disabled for GPU profiling - fix segfault (within CUDA's code) on old, buggy CUDA driver versions - cudaprofiler: use (and ship) CUPTI 13 if runtime CUDA version supports it - ingest: fix unbalanced metrics sharding - helm: adjust service config to route by request, not connection - ensures that MCP requests are always routed to the same web replica - ui: integrate API explorer into the web app - ui: sort "[__other__]" entry to the bottom in metric charts/tooltips ## v25.12.6 - profiler: add module names to Python `` frames - profiler: rework stack unwinding mechanism - can unwind stacks that are much much longer than before now - some Python / Java stack traces were cut off in the middle before - profiler: fix an issue where in rare cases the profiler could crash during shutdown when GPU metrics collection is enabled - cudaprofiler: significant performance improvements - cudaprofiler: fix possible segfault on certain CUDA versions - cudaprofiler: print version on load if logging is enabled to ease troubleshooting - web: fix off by one in `/events` endpoint in the public API (beta) - web: updated top field values endpoint with pagination support - web: fix bug that could cause simplified function names to not appear on the top function page - web: reduce histogram query memory consumption - backend: fix decoding of older license keys with `zing-jdk` feature - assistant: improve user-facing error messages - assistant: simplify custom LLM configuration - assistant: call tree + improved default prompts - ui: hide Python runtime frames by default - ui: add fullscreen mode for diff top functions view - ui: add search support for flamegraph on top functions view - ui: initial AI Assistant for CPU flamegraph - ui: add buttons to export CPU / GPU events, and profiler diagnostic data in the `Support` dialog - these can be sent to the zymtrace team for support - ui: add documentation links to agent installation steps - ui: add service and kernel version to host details page - ui: add Slack support option to `Support` dialog ## v25.12.5 - migrate: fix migration checksum checks for manually managed distributed Clickhouse deployments ## v25.12.4 - profiler: add ARM64 support for cudaprofiler - profiler: don't export PC samples by default - these are still captured, and used to disassemble instructions - but as they're high cardinality, exporting the addresses is turned off by default now, to reduce resource costs of ingest and clickhouse - this can still be enabled by running the workload with `env ZYMTRACE_CUDAPROFILER__ENABLE_PC_OFFSETS=true` - profiler: fix unwinding for PGO builds of interpreters - the previous fix for this had a small bug which is fixed now - ui: add differential functions view for GPU functions - ui: fix a bug that could cause wrong GPUs to show up on container/pod details pages - ui: fix an issue that could cause the wrong filter to be applied when using the actions inside the chart tooltip ## v25.12.3 - ui: fixed an issue that could prevent navigation from the "Top Entities" section on the Efficiency IQ page - profiler: remove special coloring of cuda launch frames ## v25.12.2 - profiler: fix bug that could cause cuda launch function frames to appear on CPU flamegraph - ui: adjust truncation of legend entries in charts to use end truncation instead of middle truncation - ui: apply sorting to tooltip entries in metric charts - ui: add select all and deselect all buttons to language filter dropdown - gateway: update Envoy to `v1.36.3` - Versions ≥ `v1.50.0` support automatically raising ulimits to their hard limit, which is important in K8S clusters using containerd ≥ 2.0, where default limits are very conservative now - storage: don't use `EXCHANGE TABLE` ClickHouse DDL - allows deploying ClickHouse on filesystems without support for atomic file renames (`renameat2`) - ui: display CPU utilization metrics in cores instead of percentages ## v25.12.1 - ui: add light mode support - ui: show description of SASS instruction on hover for GPU profiles - ui: fixed an issue that could cause chart legend entries to not be truncated correctly in Firefox - ui: enable transport compression for flamegraph WASM blob - backend: replace gRPC health checks with HTTP health checks - symdb: retry auto upload for broken executables after a day - interval can be configured via `SYMDB__BROKEN_EXECUTABLE_RETRY_AFTER` env variable (in seconds) - profiler: make auto upload size limits configurable - these limits only apply if `-dwarf` is given - `ZYMTRACE_MAX_SYMBFILE_SIZE` (for the symbol file) and `ZYMTRACE_MAX_INPUT_FILE_SIZE` (for the size of the binary file that we extract symbols from) can be used to configure this - both are in bytes - profiler: add support for NVIDIA MIG devices (for metrics) - profiler: fix bug that could cause available compute graph (and idle time of a machine) to drop significantly, if GPU profiling was active and GPU was heavily used - cudaprofiler: fix rare segfaults in CUDA caused by incompatible version of CUPTI already being loaded in some processes - PyTorch based processes sometimes would load an incompatible libcupti.so, which could lead to segfaults - cudaprofiler: enable sampling of kernel launches by default - cudaprofiler: expose stack trace sampling config via env vars ## v25.11.6 - mcp: improve time ranges and limit retries if no data found - profiler: fix unwinding for LTO/PGO builds of interpreters - `.cold` parts of a split interpreter loop are now supported - profiler: add source location mapping for GPU profiling with PC sampling - profiler: add arguments to control how NVML is located - `-nvml-path` allows the NVML path to be specified explicitly - `-nvml-auto-scan` allows opting into an automatic scan for the NVML library - profiler: fixed an issue where in rare cases the profiler could crash during symbol extraction - cudaprofiler: flush PC samples more frequently, and track hardware buffer being full - cudaprofiler: support disassembling more SASS instructions - web/profiler: allow top GPU stall reasons to be viewed in top functions list - ui: show description of stall reason in tooltip for GPU flamegraph - makes the stall reasons more descriptive, and offers possible solutions for reducing their impact - ui: improve click-to-copy handling for text elements - ui: add script/container name support for GPU consumer metrics - ui: persist aggregation and language settings in both URL and local storage - allows sharing links without overwriting local settings; conflicting settings can be accepted, discarded, or kept diverged ## v25.11.5 - profiler: fix bug that caused GPU implant instrumentation to not work when profiler runs in docker container ## v25.11.4 - profiler: support zing 25.01 with JDK 11 and 17 (21 was already supported) - symblib: fix Rust function name demangling which failed in some cases - mcp: suggest using local time instead of UTC - profiler: reduce allocations in parseFDE() - profiler: place uprobes on GPU implant dynamically (https://github.com/zystem-io/zymtrace/pull/1453) - implant no longer needs to be mapped into the kubernetes container (for the profiler) - ui: fix rendering bug in flamegraph where child node could be wider than its parent - cudaprofiler: several improvements (https://github.com/zystem-io/zymtrace/pull/1448) - fix bug with force flushing incomplete kernels - don't delay process synchronizations during startup for CUDA processes, leading to better CPU stack traces for the first few frames - support building with CUDA 13 - allow to bypass GPU presence checks in profiler - profiler: support zing 25.01 with jdk 11 and 17 (21 was already supported) (https://github.com/zystem-io/zymtrace/pull/1441) - symblib: Fix Rust function name demangling which failed in some cases - mcp: Suggest using local time instead of UTC - profiler: Reduce allocations in parseFDE() - brings parseFDE down from 60% of all allocs to 0.3% - mcp: enable `collapse_go_system_frames`, `collapse_jvm_threads`, `filter_error_events`, `filter_unreported` to reduce number of tokens in the flamegraph response - ui: add new display option `collapse_go_system_frames` - default enabled and inverted in UI: "Show Go system frames" - aggregates GC frames into "Garbage Collector" - aggregates scheduler frames into "Scheduler" - ui: fix frame filter for java standard library functions - it was previously filtering out functions too aggressively - profiler: improve zing symbolization - adds support for the GC in zing, leading to more debug information being resolved, and thus deeper / longer stack traces - profiler: place uprobes on GPU implant dynamically - implant is now detected and instrumented regardless of its path - cudaprofiler: several improvements - improve flushing of CUPTI activity records - don't delay process synchronizations during startup for CUDA processes, leading to better CPU stack traces for the first few frames - support building with CUDA 13 - allow to bypass GPU presence checks in profiler ## v25.11.3 - mcp: change flamegraph culling to root-based culling as in the UI - ui: new display option 'filter_error_frames' - filters error frames by default, thus allows the profiler to send error frames by default to improve CPU usage accuracy - all: add oidc/local auth support along with service tokens ## v25.11.2 - profiler: increase the maximum number of unwound frames from 128 to 256 - this avoids unwind errors with long stack traces and thus improves CPU attribution - profiler: improve log message when falling back from BTF to binary analysis - profiler: print out system info if attaching to tracepoints fails - mcp: add topentities as tool, resource and resource template ## v25.11.0 - profiler: reworked PID reporting mechanism, significantly reducing CPU usage - Especially high impact on systems that spawn many short-lived processes - profiler: prefer DWARF symbols over Go symbols during automatic symbol upload - Improves symbol quality for Go executables with DWARF debug info when running the profiler with `-dwarf` argument - profiler: more efficient stack delta extraction for native executables - Significantly reduces the peak memory usage of the profiler in the presence of large native executables - ui: add new display option `collapse_jvm_threads` - aggregates GC threads into "Garbage Collector" (see with grouped by "Thread Name" in the flamegraph) - aggregates all GC frames into a single one called "Garbage Collector" - also aggregates JVM JIT frames and threads in the same fashion - turned on by default ## v25.10.12 - profiler: optimize performance ## v25.10.11 - ui: switch to relative mode if `matches` is used on column incompatible with `absolute` mode ## v25.10.10 - profiler: fixed an issue that prevented the script name attribute to be set for GPU traces - ui: support `matches` for regex matches in advanced query mode ## v25.10.9 - web: better errors if regex syntax is invalid in `matches` CEL query - backend: add local login and RBAC support with CRUDs - profiler: fix version matching logic for zing offsets - profiler: add support for more zing versions - we now additionally support these versions - JDK 1.8.X with zing 24.02.X - JDK 1.8.X with zing 24.08.X - JDK 1.8.X with zing 25.02.X - JDK 11.0.X with zing 23.02.X - JDK 11.0.X with zing 23.08.X - JDK 11.0.X with zing 24.02.X - JDK 11.0.X with zing 24.08.X - JDK 11.0.X with zing 25.02.X - JDK 17.0.X with zing 24.02.X - JDK 21.0.X with zing 24.01.X - JDK 21.0.X with zing 24.03.X - JDK 21.0.X with zing 24.04.X - JDK 21.0.X with zing 24.05.X - JDK 21.0.X with zing 24.07.X - JDK 21.0.X with zing 24.09.X - JDK 21.0.X with zing 24.10.X - JDK 21.0.X with zing 24.12.X - JDK 21.0.X with zing 25.01.X ## v25.10.8 - ui: fix possible crash during flamegraph rendering ## v25.10.7 - profiler: add support for OpenJDK 25 - profiler: fix retry logic for fetching container name(s) - gpu: support `CUkernel`s properly in addition to `CUfunc`s - ui: add detail pages for `namespace`, `pod` and `deployment` - profiler: fix aggregations for metrics (script names were wrongly merged) --- # aws-aurora URL: https://docs.zymtrace.com/databases/aws-aurora import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; # AWS Aurora PostgreSQL Setup This guide covers setting up AWS Aurora PostgreSQL with IAM authentication for Zymtrace on Amazon EKS. :::info Disclaimer Most of the instructions in this guide are specific to AWS setup rather than Zymtrace itself. We've documented our own experience setting up AWS Aurora with EKS to make it easier for users who want to use this configuration. The zymtrace-specific configuration is minimal - it's primarily about connecting to your PostgreSQL database once it's properly configured in AWS. ::: This guide covers setting up AWS Aurora PostgreSQL with IAM authentication for Zymtrace on Amazon EKS. ## Prerequisites 1. AWS EKS cluster with OIDC provider configured 2. Aurora PostgreSQL cluster with IAM authentication enabled 3. IAM role with RDS connect permissions 4. Kubernetes service account annotated with IAM role ARN ## Setup Steps ### Step 1: Enable IAM Authentication on Aurora Enable IAM database authentication on your Aurora PostgreSQL cluster: ```bash aws rds modify-db-cluster \ --db-cluster-identifier your-aurora-cluster \ --enable-iam-database-authentication ``` Or via the AWS Console: - Navigate to RDS → Databases → Your Aurora Cluster - Modify → Additional configuration → Database authentication - Enable "IAM database authentication" ### Step 2: Create Database User and Configure Permissions Connect to Aurora using the master user and create the IAM-enabled database user. There are two approaches for setting up database permissions, depending on whether you want to use automatic database creation or manual database creation. #### Option 1: Using autoCreateDBs Mode If you plan to use `autoCreateDBs: true` in your Helm configuration, you only need to grant the `CREATEDB` permission. The required databases will be created automatically by Zymtrace on startup. ```sql -- Create IAM database user with CREATEDB permission CREATE USER zymtrace_user LOGIN CREATEDB; GRANT rds_iam TO zymtrace_user; ``` #### Option 2: Manual Database Creation If you prefer to create the databases manually (using `autoCreateDBs: false`), follow these steps: ```sql -- Create IAM database user CREATE USER zymtrace_user LOGIN; GRANT rds_iam TO zymtrace_user; -- Create the three required databases CREATE DATABASE zymtrace_identity; CREATE DATABASE zymtrace_symdb; CREATE DATABASE zymtrace_web; -- Grant all privileges on databases GRANT ALL PRIVILEGES ON DATABASE zymtrace_identity TO zymtrace_user; GRANT ALL PRIVILEGES ON DATABASE zymtrace_symdb TO zymtrace_user; GRANT ALL PRIVILEGES ON DATABASE zymtrace_web TO zymtrace_user; ``` Grant schema privileges for each database: ```sql -- For zymtrace_identity database \c zymtrace_identity GRANT ALL ON SCHEMA public TO zymtrace_user; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO zymtrace_user; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO zymtrace_user; -- Repeat for zymtrace_symdb and zymtrace_web \c zymtrace_symdb GRANT ALL ON SCHEMA public TO zymtrace_user; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO zymtrace_user; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO zymtrace_user; \c zymtrace_web GRANT ALL ON SCHEMA public TO zymtrace_user; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO zymtrace_user; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO zymtrace_user; ``` :::tip Database Permissions The database user needs ALL privileges on the schema for migrations to work properly. The `ALTER DEFAULT PRIVILEGES` commands ensure that future tables and sequences created by Zymtrace will have the correct permissions. ::: ### Step 3: Create IAM Policy Get your Aurora cluster resource ID: ```bash aws rds describe-db-clusters \ --db-cluster-identifier your-aurora-cluster \ --query 'DBClusters[0].DbClusterResourceId' \ --output text ``` This outputs something like: `cluster-ABCDEFGHIJKL01234` Create IAM policy file `aurora-connect-policy.json`: ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "rds-db:connect", "Resource": "arn:aws:rds-db:::dbuser:/zymtrace_user" } ] } ``` Replace: - `` with your AWS region (e.g., `us-west-2`) - `` with your AWS account ID - `` with the cluster resource ID from above Create the policy: ```bash aws iam create-policy \ --policy-name ZymtraceAuroraConnectPolicy \ --policy-document file://aurora-connect-policy.json ``` ### Step 4: Create IAM Role with OIDC Trust Policy The OIDC role is what allows your Kubernetes pods to authenticate as an AWS IAM role without storing any AWS credentials. Here's how it works: 1. Some Zymtrace pods need to access Aurora with IAM authentication 2. IAM authentication requires AWS credentials to generate database auth tokens 3. OIDC is the secure bridge that lets Kubernetes service accounts assume AWS IAM roles 4. The flow: - Pod uses Kubernetes service account `zymtrace-aurora-sa` - Service account has annotation pointing to AWS IAM role - EKS OIDC provider tells AWS "this Kubernetes service account is allowed to assume this IAM role" - Pod gets temporary AWS credentials to generate RDS IAM auth tokens - Pod uses those tokens to connect to Aurora Get your EKS cluster's OIDC provider: ```bash aws eks describe-cluster \ --name your-cluster-name \ --query "cluster.identity.oidc.issuer" \ --output text ``` This outputs something like: `https://oidc.eks.us-west-2.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE` Create trust policy file `trust-policy.json`: ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Federated": "arn:aws:iam:::oidc-provider/oidc.eks..amazonaws.com/id/" }, "Action": "sts:AssumeRoleWithWebIdentity", "Condition": { "StringEquals": { "oidc.eks..amazonaws.com/id/:sub": "system:serviceaccount::zymtrace-aurora-sa", "oidc.eks..amazonaws.com/id/:aud": "sts.amazonaws.com" } } } ] } ``` Replace: - `` with your AWS account ID - `` with your AWS region (e.g., `us-west-2`) - `` with the ID from the OIDC issuer URL (the part after `/id/`) - `` with your Kubernetes namespace (e.g., `zymtrace`) Create the IAM role: ```bash # Create role aws iam create-role \ --role-name zymtrace-aurora-role \ --assume-role-policy-document file://trust-policy.json # Attach policy aws iam attach-role-policy \ --role-name zymtrace-aurora-role \ --policy-arn arn:aws:iam:::policy/ZymtraceAuroraConnectPolicy ``` ### Step 5: Choose Authentication Method You have two options for providing IAM credentials to the Zymtrace pods: #### IRSA (IAM Roles for Service Accounts) This approach provides fine-grained, pod-level IAM permissions using Kubernetes service accounts. **Benefits:** - Granular permissions per service account - Better security isolation - Follows AWS best practices Create the service account with IAM role annotation: ```bash kubectl create serviceaccount zymtrace-aurora-sa -n kubectl annotate serviceaccount zymtrace-aurora-sa -n \ eks.amazonaws.com/role-arn=arn:aws:iam:::role/zymtrace-aurora-role ``` Verify the service account: ```bash kubectl get serviceaccount zymtrace-aurora-sa -n -o yaml ``` Should show the annotation: ```yaml metadata: annotations: eks.amazonaws.com/role-arn: arn:aws:iam:::role/zymtrace-aurora-role ``` In your Helm values, reference the service account: ```yaml postgres: mode: "aws_aurora" aws_aurora: host: "your-cluster.cluster-xxxxx.eu-west-2.rds.amazonaws.com" user: "zymtrace_user" database: "zymtrace" region: "eu-west-2" autoCreateDBs: false serviceAccount: "zymtrace-aurora-sa" ``` #### Node-Level IAM This approach assigns the IAM role directly to your EKS worker nodes. All pods on those nodes inherit the same permissions. This approach is simple and generally recommended for development/testing environments where all pods can share the same permissions. **Setup steps:** 1. Get your node group's IAM role: ```bash aws eks describe-nodegroup \ --cluster-name \ --nodegroup-name \ --query 'nodegroup.nodeRole' \ --output text ``` This outputs something like: `arn:aws:iam::123456789012:role/eksctl-cluster-nodegroup-ng-NodeInstanceRole-ABC123` 2. Attach the Aurora connect policy to the node role: ```bash aws iam attach-role-policy \ --role-name \ --policy-arn arn:aws:iam:::policy/ZymtraceAuroraConnectPolicy ``` 3. In your Helm values, leave the `serviceAccount` field empty: ```yaml postgres: mode: "aws_aurora" aws_aurora: host: "your-cluster.cluster-xxxxx.eu-west-2.rds.amazonaws.com" user: "zymtrace_user" database: "zymtrace" region: "eu-west-2" autoCreateDBs: false serviceAccount: "" # Empty - pods will use node IAM role ``` :::warning Security Note This gives Aurora access to ALL pods on the worker nodes, not just Zymtrace. ::: :::tip Using Node-Level IAM If you assigned the IAM role directly to your worker nodes (Option B in Step 5), set `serviceAccount: ""` or omit it entirely. The pods will automatically use the node's IAM role to authenticate to Aurora. ::: Deploy Zymtrace: ```bash helm upgrade --install backend zymtrace/backend \ -f your-values.yaml \ --namespace \ --debug \ --atomic ``` ## Verification Check the migration job logs: ```bash kubectl logs -l app.kubernetes.io/component=migrate -n ``` Check service pods: ```bash kubectl get pods -n ``` All pods should be running and using the `zymtrace-aurora-sa` service account. ## Troubleshooting ### Connection fails with "no password supplied" - Verify service account exists and has IAM role annotation - Check trust policy matches service account namespace - Verify pod is using the correct service account: ```bash kubectl get pod POD_NAME -o jsonpath='{.spec.serviceAccountName}' ``` ### IAM authentication failed - Verify database user has `rds_iam` role granted: ```sql SELECT usename, useconfig FROM pg_user WHERE usename = 'zymtrace_user'; ``` - Check IAM policy has correct cluster resource ID - Ensure OIDC provider is configured on EKS cluster --- # clickhouse URL: https://docs.zymtrace.com/databases/clickhouse # ClickHouse Configuration ```mdx-code-block import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; ``` ClickHouse serves as the primary analytics database for storing profiling data and metrics. It provides high-performance columnar storage optimized for analytical queries. ## Data Retention The Helm chart renders the `clickhouse.retention` value into a retention YAML file and mounts it into the database migration job. The migration job applies the file after schema migrations on every install and upgrade, updating TTL clauses and related MergeTree settings on existing profiling and metrics tables. The default Helm configuration deletes profiling data, metrics, and settled recommendations after 720 hours (30 days): ```yaml clickhouse: retention: common: delete: { after-hours: 720 } recommendations: delete: { after-hours: 720 } ``` Set `delete: disabled` to retain a section's data indefinitely. ClickHouse processes expired data asynchronously during merges, so rows can remain visible after the configured time has elapsed. ### Retention sections The configuration requires `common` and `recommendations` sections: - `common` configures profiling and metrics tables. - `recommendations` configures recommendations tables. The optional top-level `apply` key controls how the migration job runs retention `ALTER` statements. `sync` (the default) waits for every replica and fails the migration once ClickHouse's `distributed_ddl_task_timeout` elapses (180 seconds by default). `async` submits the statements without waiting. Use `async` when TTL changes on large tables or clusters exceed the timeout. The following example applies retention asynchronously, deletes common data after 7 days, moves it to the `cold` volume after 1 day, and recompresses it with ZSTD level 7 after 3 days. Recommendations remain for 60 days. ```yaml clickhouse: retention: apply: async common: policy: tiered delete: { after-hours: 168 } tiers: - move: { after-hours: 24, to: { volume: cold } } - recompress: { after-hours: 72, codec: { zstd: 7 } } settings: merge-with-ttl-timeout-sec: 14400 ttl-only-drop-parts: true recommendations: delete: { after-hours: 1440 } ``` `policy` must name a ClickHouse storage policy available on every target server. Each `move` destination must be a disk or volume defined by that policy. :::warning ClickHouse Cloud ClickHouse Cloud does not support custom storage policies or TTL `TO DISK`/`TO VOLUME` moves, so the `policy` and `tiers[].move` options only apply to self-hosted deployments. ClickHouse Cloud manages data tiering automatically; see [Implementing a hot/warm/cold architecture](https://clickhouse.com/docs/concepts/features/operations/delete/ttl#implementing-a-hotwarmcold-architecture). `delete` retention works on all deployments. ::: | Field | Values | Behavior | |-----------------------------------------|------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------| | `apply` (top-level) | `sync` (default) or `async` | `sync` waits for retention `ALTER`s on every replica; `async` submits them without waiting. | | `policy` | ClickHouse storage policy name | Sets the table's `storage_policy`. Required when tier rules move data outside the current policy. | | `delete` | `disabled` or `{ after-hours: HOURS }` | Removes expired data or retains it indefinitely. `HOURS` must be a positive integer. | | `tiers[].move` | `{ after-hours: HOURS, to: { volume: NAME } }` or `{ after-hours: HOURS, to: { disk: NAME } }` | Moves data to a ClickHouse volume or disk. | | `tiers[].recompress` | `{ after-hours: HOURS, codec: CODEC }` | Recompresses data with `lz4`, `{ lz4hc: LEVEL }` (`1`–`12`), or `{ zstd: LEVEL }` (`1`–`22`). | | `settings.merge-with-ttl-timeout-sec` | Non-negative integer | Sets the minimum delay between delete-TTL merges. ClickHouse defaults to 14400 seconds. | | `settings.ttl-only-drop-parts` | Boolean | Drops complete expired parts instead of deleting rows where supported. Defaults to `true`. | | `settings.materialize-ttl-after-modify` | Boolean | Rewrites existing parts right after TTL rules change. Defaults to `true`. | ### Existing data after TTL changes By default, ClickHouse rewrites every existing part as soon as the migration job changes a section's TTL rules, so the new rules apply to existing data immediately. On large tables this rewrite is expensive. Set `settings.materialize-ttl-after-modify: false` on a section to skip it: newly written and merged parts follow the new rules, while existing parts keep the expiry they were written with until a merge rewrites them. :::warning With `materialize-ttl-after-modify: false`, increasing a `delete` offset does not protect existing data: parts written under the old rules are still deleted at the old offset. Parts written before the table had any TTL rule do not expire until a merge rewrites them. ::: ### Override retention settings Helm deep-merges `clickhouse.retention` with the chart defaults, so a values file only needs the fields it changes. Overriding `common` keeps the default `recommendations` section, and vice versa. Individual fields can also be set on the command line: ```bash helm upgrade --install backend zymtrace/backend \ --namespace zymtrace \ --set clickhouse.retention.common.delete.after-hours=168 ``` Misspelled section names and sections without a `delete` rule fail at template time; the migration job validates the remaining fields before applying any changes. ### Skip retention management Set `global.skipClickHouseRetention: true` to stop the migration job from changing TTL rules and the related table settings on existing tables, for example when you manage TTLs directly in ClickHouse. Schema migrations still run, and `clickhouse.retention` is ignored. ```bash helm upgrade --install backend zymtrace/backend \ --namespace zymtrace \ --set global.skipClickHouseRetention=true ``` ## Configuration Modes ### Connect to existing ClickHouse This mode connects to your existing ClickHouse cluster, including ClickHouse Cloud, on-premises clusters, and single-node instances. **Supported ClickHouse Deployments** - **ClickHouse Cloud**: Managed ClickHouse service with automatic scaling and management - **ClickHouse Clusters**: Multi-node distributed setups with shards and replicas - **Single-node ClickHouse**: Standalone ClickHouse server installations ## Database Setup Options You have two options for setting up the required databases: 1. **Manual Setup**: Create databases and users manually using SQL commands. 2. **Automatic Setup**: Enable `autoCreateDBs` to let Zymtrace create databases during migration ### Option 1: Manual Database Setup When connecting to an existing ClickHouse cluster, we strongly recommend that you **create a dedicated user, assign appropriate permissions**, and create a new database for `zymtrace`. If you're using a distributed ClickHouse cluster with multiple shards/replicas, you have two options: 1. **Use the `{cluster}` macro** (recommended): A common convention where `{cluster}` is configured as a macro that resolves to your cluster name 2. **Find the actual cluster name**: Query your ClickHouse installation to get the specific cluster name ```sql -- Check if the {cluster} macro is available SELECT * FROM system.macros; -- Find available clusters in your ClickHouse installation SELECT cluster FROM system.clusters; ``` #### SQL Commands for Database Setup Choose the appropriate commands based on your ClickHouse deployment. :::danger **Caution**: This script will drop the `zymtrace_profiling` database if it exists. Run it only if this is your first time setting up Zymtrace with ClickHouse, or if you intentionally want to reset the database, knowing that this will permanently delete any existing data. ::: ##### ClickHouse Cloud and Single Node For single-node ClickHouse installations or ClickHouse Cloud: ```sql -- Clean slate: remove any existing profiling database DROP DATABASE IF EXISTS zymtrace_profiling SYNC; DROP DATABASE IF EXISTS zymtrace_metrics SYNC; CREATE DATABASE zymtrace_profiling; CREATE DATABASE zymtrace_metrics; CREATE USER IF NOT EXISTS zymtrace_user IDENTIFIED WITH sha256_password BY 'YOUR NEW PASSWORD HERE'; GRANT SELECT, INSERT, UPDATE, ALTER, DELETE, CREATE, DROP, SHOW, OPTIMIZE, TRUNCATE, SYSTEM SYNC REPLICA ON zymtrace_profiling.* TO zymtrace_user; GRANT SELECT, INSERT, UPDATE, ALTER, DELETE, CREATE, DROP, SHOW, OPTIMIZE, TRUNCATE, SYSTEM SYNC REPLICA ON zymtrace_metrics.* TO zymtrace_user; ``` Note that `SYSTEM SYNC REPLICA` is only strictly required when running in ClickHouse Cloud and optional when using a self-hosted single node instance. ##### Distributed ClickHouse Cluster Setup For ClickHouse clusters with multiple shards/replicas, you can use either the `{cluster}` macro or specify the actual cluster name: **Using the `{cluster}` macro (recommended)** ```sql -- Clean slate: remove any existing profiling database DROP DATABASE IF EXISTS zymtrace_profiling ON CLUSTER '{cluster}' SYNC; DROP DATABASE IF EXISTS zymtrace_metrics ON CLUSTER '{cluster}' SYNC; CREATE DATABASE zymtrace_profiling ON CLUSTER '{cluster}'; CREATE DATABASE zymtrace_metrics ON CLUSTER '{cluster}'; CREATE USER IF NOT EXISTS zymtrace_user ON CLUSTER '{cluster}' IDENTIFIED WITH sha256_password BY 'YOUR NEW PASSWORD HERE'; GRANT ON CLUSTER '{cluster}' SELECT, INSERT, UPDATE, ALTER, DELETE, CREATE, DROP, SHOW, OPTIMIZE, TRUNCATE, SYSTEM SYNC REPLICA ON zymtrace_profiling.* TO zymtrace_user; GRANT ON CLUSTER '{cluster}' SELECT, INSERT, UPDATE, ALTER, DELETE, CREATE, DROP, SHOW, OPTIMIZE, TRUNCATE, SYSTEM SYNC REPLICA ON zymtrace_metrics.* TO zymtrace_user; GRANT ON CLUSTER '{cluster}' REMOTE, CLUSTER ON *.* to zymtrace_user; ```
Alternative: Using the actual cluster name Replace `your_cluster_name` with your actual cluster name from the `system.clusters` query above: ```sql -- Clean slate: remove any existing profiling database DROP DATABASE IF EXISTS zymtrace_profiling ON CLUSTER your_cluster_name SYNC; DROP DATABASE IF EXISTS zymtrace_metrics ON CLUSTER your_cluster_name SYNC; CREATE DATABASE zymtrace_profiling ON CLUSTER your_cluster_name; CREATE DATABASE zymtrace_metrics ON CLUSTER your_cluster_name; CREATE USER IF NOT EXISTS zymtrace_user ON CLUSTER your_cluster_name IDENTIFIED WITH sha256_password BY 'YOUR NEW PASSWORD HERE'; GRANT ON CLUSTER your_cluster_name SELECT, INSERT, UPDATE, ALTER, DELETE, CREATE, DROP, SHOW, OPTIMIZE, TRUNCATE, SYSTEM SYNC REPLICA ON zymtrace_profiling.* TO zymtrace_user; GRANT ON CLUSTER your_cluster_name SELECT, INSERT, UPDATE, ALTER, DELETE, CREATE, DROP, SHOW, OPTIMIZE, TRUNCATE, SYSTEM SYNC REPLICA ON zymtrace_metrics.* TO zymtrace_user; GRANT ON CLUSTER your_cluster_name REMOTE, CLUSTER ON *.* to zymtrace_user; ```
:::tip Cluster Configuration - **`{cluster}` macro**: A common naming convention where `{cluster}` is configured as a macro that resolves to your cluster name. This approach works across different cluster configurations without hardcoding names. - **Actual cluster name**: Replace `your_cluster_name` with the actual cluster name from your `system.clusters` query. Common examples include `production_cluster`, `default_cluster`, or `ch_cluster`. :::
Make sure to replace `YOUR NEW PASSWORD HERE` with a secure password. Reference - https://clickhouse.com/docs/sql-reference/statements/grant ### Option 2: Automatic Database Setup Alternatively, you can enable `autoCreateDBs: true` in your configuration to let Zymtrace automatically create the required databases during migration. This option requires that your ClickHouse user has CREATE permissions on the server. **When to use automatic setup:** - Development environments where quick setup is preferred - When you have administrative access to grant broad CREATE permissions - Testing scenarios where database recreation is acceptable **When to use manual setup:** - Production environments requiring strict permission control - When following security best practices with minimal required permissions - Enterprise environments with database administration policies :::tip info When using ClickHouse Cloud, ensure you use port `8443` and enable secure connection. For ClickHouse clusters, specify the cluster name to enable distributed queries. ::: ### Helm Configuration #### ClickHouse Cloud / Single Node This configuration applies to both ClickHouse Cloud and standalone single-node ClickHouse installations. ```yaml clickhouse: mode: "use_existing" use_existing: host: "https://abc123def.us-east-1.aws.clickhouse.cloud:8443" # For ClickHouse Cloud, # OR "http://clickhouse.internal.company.com:8123" # For single-node user: "default" # For Cloud, or "zymtrace_user" for single-node password: "your-cloud-password" # For Cloud, or "secure-password" for single-node database: "zymtrace" secure: true # For Cloud, or false/true for single-node depending on TLS # clusterName not needed for Cloud or single-node # autoCreateDBs: true # or create the DBs yourself using the SQL above ``` #### ClickHouse Cluster (On-premises) This configuration applies to distributed ClickHouse clusters with multiple nodes, shards, or replicas. ```yaml clickhouse: mode: "use_existing" use_existing: host: "https://your-instance.region.provider.clickhouse.cloud:8443" # Complete URL with protocol and port user: "default" # Your ClickHouse username password: "your-password" # Your ClickHouse password database: "zymtrace" # Database prefix - creates zymtrace_profiling and zymtrace_metrics secure: true # Enable TLS/secure connection (required for ClickHouse Cloud) clusterName: "" # Optional: ClickHouse cluster name for distributed setups (e.g., "production_cluster"). # Required when using ClickHouse clusters with multiple shards/replicas. # You can use either the actual cluster name or "{cluster}" macro if configured. # When enabled, we use the ON CLUSTER macro for distributed queries across cluster nodes. # Leave empty for ClickHouse Cloud or single-node ClickHouse instances. autoCreateDBs: false # When true, zymtrace migration will automatically create the required databases. # NOTE: For autoCreateDBs to work, the database user must have CREATE DATABASE permission. # Grant with: GRANT CREATE ON *.* TO your_user; ``` #### HTTP Interface Requirement :::warning Protocol Requirement Only the ClickHouse HTTP interface is supported due to limitations in the official ClickHouse Rust client. The native protocol port is not supported. Always use the HTTP interface port: - **HTTP**: Port 8123 (typically for internal/dev environments) - **HTTPS**: Port 8443 (recommended for production and ClickHouse Cloud) ::: #### URL Format Requirements The `host` field must include: - **Protocol**: `http://` or `https://` - **Hostname/IP**: The ClickHouse server address - **Port**: The HTTP interface port ### Next Steps After configuring ClickHouse, proceed to configure the other storage components: [Configure Postgres](./postgres.md) | [Configure Object Storage](./object-storage.md)
### Deploy new ClickHouse instance This mode deploys and manages ClickHouse within your cluster.
ClickHouse Create Mode Configuration ```yaml clickhouse: mode: "create" create: image: repository: clickhouse/clickhouse-server tag: "25.3.2.39" config: user: "clickhouse" password: "clickhouse123" database: "zymtrace" service: http: port: 8123 native: port: 9000 replicas: 1 resources: requests: cpu: "500m" memory: "1Gi" limits: cpu: "2000m" memory: "4Gi" storage: type: "persistent" # "persistent" or "empty_dir" size: 30Gi className: "" ```
--- # Google Cloud SQL Proxy URL: https://docs.zymtrace.com/databases/gcp-cloudsql # Google Cloud SQL with IAM Authentication for GKE :::info Disclaimer Most of the instructions in this guide are specific to Google Cloud Platform (GCP) infrastructure setup rather than Zymtrace itself. We've documented our own experience setting up Cloud SQL with GKE to make it easier for users who want to use this configuration. The zymtrace-specific configuration is minimal - it's primarily about connecting to your PostgreSQL database once it's properly configured in GCP. ::: The Cloud SQL Proxy acts as a secure tunnel, handling IAM authentication and encryption transparently while providing Zymtrace with a standard PostgreSQL connection. This guide demonstrates how to set up Google Cloud SQL PostgreSQL with IAM authentication for use with GKE clusters. This configuration provides secure, password-less database access using Google Cloud's Workload Identity. :::tip Info This document assumes you're setting up Cloud SQL from scratch. If you already have an existing Cloud SQL instance or GKE cluster, feel free to skip to the relevant sections that apply to your setup. ::: ## Overview ### What You'll Accomplish By following this guide, you will: - Create a Cloud SQL PostgreSQL instance with IAM authentication - Set up Workload Identity between GKE and Cloud SQL - Configure automatic database creation (optional) - Deploy Zymtrace that securely connects to Cloud SQL without managing passwords ### Architecture ``` ┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐ │ GKE Pod │───▶│ Cloud SQL Auth │───▶│ Cloud SQL │ │ │ │ Proxy │ │ PostgreSQL │ │ zymtrace │ │ │ │ │ │ K8s SA │ │ IAM Auth │ │ IAM User │ └─────────────────┘ └──────────────────┘ └─────────────────┘ │ │ │ └────────────────────────┼───────────────────────┘ Workload Identity Binding ``` ## Prerequisites Before you begin, ensure you have: | Requirement | Description | |-------------|-------------| | **GKE Cluster** | Running with Workload Identity enabled (or ability to enable it) | | **CLI Tools** | `gcloud` and `kubectl` installed and authenticated | | **Permissions** | IAM roles for Cloud SQL, GKE, and service account management | ### Required IAM Roles Your user account needs these roles: - `roles/cloudsql.admin` - To create and manage Cloud SQL instances - `roles/container.admin` - To manage GKE clusters and node pools - `roles/iam.serviceAccountAdmin` - To create and manage service accounts - `roles/resourcemanager.projectIamAdmin` - To bind IAM policies ## Configuration ### Environment Setup First, set up your environment variables. Replace the example values with your actual project details: ```bash # Project and instance configuration export PROJECT_ID="your-project-id" export REGION="us-central1" export INSTANCE_NAME="zymtrace-postgres" export DATABASE_VERSION="POSTGRES_17" # GKE configuration export CLUSTER_NAME="your-gke-cluster" export NAMESPACE="your-namespace" # Service account configuration export SA_NAME="cloudsql-proxy-sa" export K8S_SA_NAME="cloudsql-k8s-sa" ``` ## Implementation Steps ### Step 1: Create Cloud SQL Instance Create a new PostgreSQL instance with recommended settings for production use: ```bash gcloud sql instances create $INSTANCE_NAME \ --database-version=$DATABASE_VERSION \ --region=$REGION \ --cpu=2 \ --memory=7680MB \ --storage-size=50GB \ --storage-type=SSD \ --storage-auto-increase \ --enable-bin-log \ --maintenance-release-channel=production \ --deletion-protection \ --project=$PROJECT_ID ``` Set a password for the default postgres user (required for initial setup): ```bash gcloud sql users set-password postgres \ --instance=$INSTANCE_NAME \ --password="$(openssl rand -base64 32)" ``` :::tip The postgres password is only used for initial database setup. Zymtrace will use IAM authentication. ::: ### Step 2: Configure Service Accounts Create a Google Cloud service account for Cloud SQL access: ```bash # Create the service account gcloud iam service-accounts create $SA_NAME \ --display-name="zymtrace Cloud SQL Proxy Service Account" \ --description="Enables zymtrace IAM authentication to Cloud SQL from GKE" # Grant required Cloud SQL permissions gcloud projects add-iam-policy-binding $PROJECT_ID \ --member="serviceAccount:$SA_NAME@$PROJECT_ID.iam.gserviceaccount.com" \ --role="roles/cloudsql.client" gcloud projects add-iam-policy-binding $PROJECT_ID \ --member="serviceAccount:$SA_NAME@$PROJECT_ID.iam.gserviceaccount.com" \ --role="roles/cloudsql.instanceUser" ``` ### Step 3: Database Setup #### Create IAM Database User Create a database user that corresponds to your service account: ```bash gcloud sql users create $SA_NAME@$PROJECT_ID.iam \ --instance=$INSTANCE_NAME \ --type=cloud_iam_service_account ``` #### Configure Database Permissions There are two approaches for setting up database permissions, depending on whether you want to use automatic database creation or manual database creation. ##### Option 1: Using autoCreateDB Mode (recommended, if possible) If you plan to use `autoCreateDBs: true` in your Helm configuration, you only need to grant the `CREATEDB` permission. The databases will be created automatically by Zymtrace on startup. Connect to your instance: ```bash # Connect as postgres user gcloud sql connect $INSTANCE_NAME --user=postgres --database=postgres ``` Run this SQL command: ```sql -- Grant CREATEDB permission (allows zymtrace to create databases automatically) ALTER USER "your-sa-name@your-project.iam" CREATEDB; -- Exit psql \q ``` That's it! When you deploy Zymtrace with `autoCreateDBs: true`, the required databases (`zymtrace_identity`, `zymtrace_symdb`, and `zymtrace_web`) will be created automatically with the correct permissions. ##### Option 2: Using Manual Database Creation Process If you prefer to manually create and manage databases (using `autoCreateDBs: false`), follow these steps: Connect to your instance: ```bash # Connect as postgres user gcloud sql connect $INSTANCE_NAME --user=postgres --database=postgres ``` Run these SQL commands: ```sql -- Step 1: Create the databases manually CREATE DATABASE zymtrace_identity OWNER your-sa-name@your-project.iam; CREATE DATABASE zymtrace_symdb OWNER your-sa-name@your-project.iam; CREATE DATABASE zymtrace_web OWNER your-sa-name@your-project.iam; -- Step 2: Alter schema \c zymtrace_identity ALTER SCHEMA public OWNER TO "your-sa-name@your-project.iam"; \c zymtrace_symdb ALTER SCHEMA public OWNER TO "your-sa-name@your-project.iam"; \c zymtrace_web ALTER SCHEMA public OWNER TO "your-sa-name@your-project.iam"; -- Exit psql \q ``` :::tip Database Creation Issues The above SQL commands don't always work consistently in Cloud SQL with IAM users. If you encounter permission errors when creating databases with the IAM user as owner, try this: Run this command first as a superuser (postgres or admin user): ```sql GRANT "your-sa-name@your-project.iam" TO postgres; ``` Then proceed with the original database creation commands. Create the databases first, then change ownership: ```sql -- Create databases without specifying owner CREATE DATABASE zymtrace_identity; CREATE DATABASE zymtrace_symdb; CREATE DATABASE zymtrace_web; -- Then alter ownership ALTER DATABASE zymtrace_identity OWNER TO "your-sa-name@your-project.iam"; ALTER DATABASE zymtrace_symdb OWNER TO "your-sa-name@your-project.iam"; ALTER DATABASE zymtrace_web OWNER TO "your-sa-name@your-project.iam"; -- Then alter schema ownership \c zymtrace_identity ALTER SCHEMA public OWNER TO "your-sa-name@your-project.iam"; \c zymtrace_symdb ALTER SCHEMA public OWNER TO "your-sa-name@your-project.iam"; \c zymtrace_web ALTER SCHEMA public OWNER TO "your-sa-name@your-project.iam"; ``` If these approaches still don't work, consider contacting GCP support. ::: :::tip info Important**: Replace `your-sa-name@your-project.iam` with your actual service account name (e.g., `cloudsql-proxy-sa@your-project.iam`). ::: ### Step 4: Configure GKE Workload Identity #### Verify Node Pool Scopes Check if your node pool has the required OAuth scopes: ```bash gcloud container node-pools describe default-pool \ --cluster=$CLUSTER_NAME \ --region=$REGION \ --format="value(config.oauthScopes)" ``` The output should include `https://www.googleapis.com/auth/cloud-platform`. :::warning Node pool scope limitation If your existing node pool doesn't have the `cloud-platform` scope, you **cannot** update it after creation. Node pool scopes are immutable. You'll need to create a new node pool with the correct scopes. ::: #### Create Node Pool with Required Scopes (if needed) If your existing node pool lacks the required scopes, create a new node pool specifically for the Cloud SQL proxy: ```bash # Create a new node pool with cloud-platform scope gcloud container node-pools create cloudsql-pool \ --cluster=$CLUSTER_NAME \ --region=$REGION \ --scopes=https://www.googleapis.com/auth/cloud-platform \ --num-nodes=1 \ --machine-type=e2-medium \ --enable-autorepair \ --enable-autoupgrade ``` Add the node selector to your custom-values.yaml to ensure the proxy pod is scheduled on this new node. ```yaml postgres: mode: "gcp_cloudsql" nodeSelector: cloud.google.com/gke-nodepool: cloudsql-pool ``` #### Enable Workload Identity If not already enabled, enable Workload Identity on your cluster: ```bash # Check current status gcloud container clusters describe $CLUSTER_NAME \ --region=$REGION \ --format="value(workloadIdentityConfig.workloadPool)" # Enable if needed (takes 10-30 minutes) gcloud container clusters update $CLUSTER_NAME \ --workload-pool=$PROJECT_ID.svc.id.goog \ --region=$REGION ``` #### Set Up Identity Binding Create Kubernetes resources and bind them to Google Cloud: ```bash # Create namespace kubectl create namespace $NAMESPACE # Create Kubernetes service account kubectl create serviceaccount $K8S_SA_NAME -n $NAMESPACE # Annotate for Workload Identity kubectl annotate serviceaccount $K8S_SA_NAME -n $NAMESPACE \ iam.gke.io/gcp-service-account=$SA_NAME@$PROJECT_ID.iam.gserviceaccount.com # Create the binding gcloud iam service-accounts add-iam-policy-binding \ --role roles/iam.workloadIdentityUser \ --member "serviceAccount:$PROJECT_ID.svc.id.goog[$NAMESPACE/$K8S_SA_NAME]" \ $SA_NAME@$PROJECT_ID.iam.gserviceaccount.com ``` ## Verification and Testing ### Verify Workload Identity Check that Workload Identity is properly configured: ```bash # Verify cluster configuration gcloud container clusters describe $CLUSTER_NAME \ --region=$REGION \ --format="value(workloadIdentityConfig.workloadPool)" # Check service account annotation kubectl get serviceaccount $K8S_SA_NAME -n $NAMESPACE -o yaml # Verify IAM binding gcloud iam service-accounts get-iam-policy $SA_NAME@$PROJECT_ID.iam.gserviceaccount.com ``` ### Test Database Connection Create a test pod to verify connectivity: ```bash kubectl run postgres-test --rm -it --image=postgres:17 -n $NAMESPACE -- bash ``` Inside the test pod, connect to the database: ```bash psql -h postgres-proxy -p 5432 -U "your-sa@your-project.iam" -d zymtrace_identity ``` Run test queries: ```sql -- Verify connection and user SELECT current_user, current_database(); ``` ## Helm Install After completing the verification and testing steps above, you need to configure your Helm deployment with the nodeSelector to ensure the Cloud SQL proxy runs on the node pool with proper scopes. ### Configure custom-values.yaml Add the nodeSelector configuration to your `custom-values.yaml`: ```yaml postgres: mode: "gcp_cloudsql" gcp_cloudsql: instance: "PROJECT_ID:REGION:INSTANCE_NAME" # e.g., "local-bebop-448118-g4:us-central1:zymtrace-cloudsql-psql-1" user: "SERVICE_ACCOUNT_NAME@PROJECT_ID.iam" # e.g., "cloudsql-proxy-sa@local-bebop-448118-g4.iam" database: "zymtrace" autoCreateDBs: true # Set to false if you manually created databases workloadIdentity: enabled: true proxy: # IMPORTANT: Use nodeSelector to ensure proxy runs on node pool with cloud-platform scope nodeSelector: cloud.google.com/gke-nodepool: cloudsql-pool serviceAccount: "cloudsql-k8s-sa" # Your Kubernetes service account name ``` ### Deploy and Restart Deploy your Helm chart with the updated configuration: ```bash # Deploy or upgrade with the new configuration helm upgrade --install backend zymtrace/backend \ -f custom-values.yaml \ -n $NAMESPACE \ --debug \ --atomic # Restart the proxy deployment to ensure it gets scheduled on the correct node pool kubectl rollout restart deployment zymtrace-postgres-proxy -n $NAMESPACE ``` ### Verify Proxy Deployment Check that the proxy is running on the correct node pool: ```bash # Check which node the proxy is running on kubectl get pods -l app=postgres-proxy -n $NAMESPACE -o wide # Verify the node has the correct node pool label kubectl get nodes -l cloud.google.com/gke-nodepool=cloudsql-pool ``` For complete YAML configuration examples, refer to the [storage configuration guide](../databases/postgres.md). :::warning Common Error Without nodeSelector If you don't configure the nodeSelector properly, you'll see authentication scope errors like: ``` 025/05/26 13:31:09 [xx:us-central1:zymtrace-cloudsql-psql-1] failed to connect to instance: failed to get instance: Refresh error: failed to get instance metadata (connection name = "xx:us-central1:zymtrace-cloudsql-psql-1"): googleapi: Error 403: Request had insufficient authentication scopes. Details: [ { "@type": "type.googleapis.com/google.rpc.ErrorInfo", "domain": "googleapis.com", "metadata": { "method": "google.cloud.sql.v1beta4.SqlConnectService.GetConnectSettings", "service": "sqladmin.googleapis.com" }, "reason": "ACCESS_TOKEN_SCOPE_INSUFFICIENT" } ] More details: Reason: insufficientPermissions, Message: Insufficient Permission ``` This happens because the proxy is running on a node pool without the `cloud-platform` scope. The nodeSelector ensures the proxy runs on the correct node pool. ::: ## Troubleshooting ### Common Issues | Issue | Symptoms | Solution | |-------|----------|----------| | **Authentication Scope Error** | "Insufficient authentication scopes" | Verify node pool has `cloud-platform` scope | | **Permission Denied** | Database connection fails | Check IAM database user and CREATEDB permission | | **Workload Identity Issues** | Service account authentication fails | Verify annotation and IAM binding | | **Proxy Connection Fails** | Cannot connect to postgres-proxy | Check proxy pod logs and configuration | ### Diagnostic Commands ```bash # Check proxy pod status kubectl get pods -l app=postgres-proxy -n $NAMESPACE # View proxy logs kubectl logs -l app=postgres-proxy -n $NAMESPACE # Test Workload Identity from pod kubectl run test-wi --rm -it --image=google/cloud-sdk:slim \ --serviceaccount=$K8S_SA_NAME -n $NAMESPACE -- \ gcloud auth list ``` ### Expected Proxy Logs When the Cloud SQL proxy is working correctly, you should see log messages similar to this: ```bash kubectl logs -n your-namespace zymtrace-postgres-proxy-6db65b7446-56nqp ``` Expected output: ``` 2025/05/26 13:39:58 Authorizing with Application Default Credentials 2025/05/26 13:39:58 [local-bebop-448118-g4:us-central1:zymtrace-cloudsql-psql-1] Listening on [::]:5432 2025/05/26 13:39:58 The proxy has started successfully and is ready for new connections! 2025/05/26 13:40:14 [local-bebop-448118-g4:us-central1:zymtrace-cloudsql-psql-1] Accepted connection from 10.56.4.1:46902 2025/05/26 13:40:14 [local-bebop-448118-g4:us-central1:zymtrace-cloudsql-psql-1] client closed the connection ``` --- # object-storage URL: https://docs.zymtrace.com/databases/object-storage # Object Storage Configuration ```mdx-code-block import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; ``` Object storage is used for storing large binary files such as debugging symbols and other artifacts. Zymtrace supports S3-compatible storage solutions including MinIO, AWS S3, and Google Cloud Storage. ## Configuration Modes ### Connect to existing S3-compatible storage This mode connects to your existing MinIO, AWS S3, or Google Cloud Storage. #### MinIO Configuration [![Artifact Hub :zymtrace backend](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/zymtrace)](https://artifacthub.io/packages/helm/zymtrace/backend?modal=values)
MinIO Configuration MinIO is a high-performance, S3-compatible object storage solution that can be deployed on-premises or in the cloud. Configure your existing MinIO instance with the following settings: ```yaml storage: mode: "use_existing" use_existing: type: "minio" minio: endpoint: "" # must be a URL, HTTP/HTTPS user: "" password: "" buckets: symbols: "zymtrace-symdb" ``` **Required fields:** - `endpoint`: Complete URL to your MinIO server (e.g., `https://minio.example.com` or `http://192.168.1.100:9000`) - `user`: MinIO access key - `password`: MinIO secret key
##### Bucket Creation ```bash mc mb minio/zymtrace-symdb ``` ##### Testing Connectivity ```bash mc config host add minio http://minio.company.com:9000 ACCESS_KEY SECRET_KEY mc ls minio/zymtrace-symdb ``` ##### Configuration Example ###### Development Environment ```yaml storage: mode: "use_existing" use_existing: type: "minio" minio: endpoint: "http://minio.dev.company.com:9000" user: "dev-access-key" password: "dev-secret-key" buckets: symbols: "zymtrace-symdb-dev" ```
#### AWS S3 Configuration [![Artifact Hub :zymtrace backend](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/zymtrace)](https://artifacthub.io/packages/helm/zymtrace/backend?modal=values) AWS S3 supports two authentication methods. Static keys are the most straightforward option. IAM authentication avoids long-lived credentials and is better suited for production EKS deployments. ##### IAM Authentication Instead of static access keys, Zymtrace can authenticate to S3 using AWS IAM — either via **IRSA** (pod-level, recommended) or the **EC2 node instance profile** (node-level, simpler for dedicated clusters). ##### Required IAM Policy Both options require the following permissions on your S3 bucket: ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket", "s3:GetBucketLocation" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME", "arn:aws:s3:::YOUR_BUCKET_NAME/*" ] } ] } ``` ##### IRSA IRSA binds an IAM role directly to a Kubernetes service account. Credentials are injected into the pod as a projected token — no IMDS hop required. Use this when your cluster is shared with other workloads. **1. Create the IAM policy and role:** ```bash ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) CLUSTER_NAME="your-cluster" NAMESPACE="your-namespace" BUCKET_NAME="your-bucket-name" # must match storage.buckets.symbols in helm values SA_NAME="zymtrace-s3-sa" REGION="us-west-2" # Get OIDC provider OIDC=$(aws eks describe-cluster --name $CLUSTER_NAME --region $REGION \ --query "cluster.identity.oidc.issuer" --output text | sed 's|https://||') # Create S3 policy S3_POLICY_ARN=$(aws iam create-policy \ --policy-name zymtrace-s3-policy \ --policy-document "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":[\"s3:GetObject\",\"s3:PutObject\",\"s3:DeleteObject\",\"s3:ListBucket\",\"s3:GetBucketLocation\"],\"Resource\":[\"arn:aws:s3:::${BUCKET_NAME}\",\"arn:aws:s3:::${BUCKET_NAME}/*\"]}]}" \ --query "Policy.Arn" --output text) # Create IAM role with IRSA trust policy ROLE_ARN=$(aws iam create-role \ --role-name zymtrace-s3-role \ --assume-role-policy-document "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Federated\":\"arn:aws:iam::${ACCOUNT_ID}:oidc-provider/${OIDC}\"},\"Action\":\"sts:AssumeRoleWithWebIdentity\",\"Condition\":{\"StringEquals\":{\"${OIDC}:sub\":\"system:serviceaccount:${NAMESPACE}:${SA_NAME}\",\"${OIDC}:aud\":\"sts.amazonaws.com\"}}}]}" \ --query "Role.Arn" --output text) aws iam attach-role-policy --role-name zymtrace-s3-role --policy-arn $S3_POLICY_ARN ``` **2. Create and annotate the service account:** ```bash kubectl create serviceaccount $SA_NAME -n $NAMESPACE kubectl annotate serviceaccount $SA_NAME \ eks.amazonaws.com/role-arn=$ROLE_ARN \ -n $NAMESPACE ``` **3. Set helm values:** ```yaml storage: mode: "use_existing" use_existing: type: "s3" s3: region: "us-west-2" useIAM: "aws" serviceAccount: "zymtrace-s3-sa" # must match SA_NAME above buckets: symbols: "your-bucket-name" ``` :::warning Aurora IAM + S3 IAM together When using both `postgres.mode: aws_aurora` and `storage.use_existing.s3.useIAM: "aws"`, application pods run under the Aurora service account (`postgres.aws_aurora.serviceAccount`). Because EKS pods can only be bound to a single service account, the S3 IAM policy must also be attached to the Aurora IAM role so those pods can access S3: ```bash aws iam attach-role-policy \ --role-name \ --policy-arn ``` Set `storage.use_existing.s3.serviceAccount` to a dedicated SA (annotated with an S3-only role) for the bucket-check job. This is an EKS constraint, not a Zymtrace requirement. If attaching both policies to the Aurora role conflicts with your least-privilege requirements, use [static keys](#static-key-configuration) or the [node instance profile](#node-instance-profile) for S3 access instead. ::: ##### Node Instance Profile Attach the S3 policy to your EKS worker node IAM role. All pods on those nodes will inherit S3 access automatically via IMDS. This is the simpler option for dedicated Zymtrace clusters where all node workloads are trusted. **1. Attach the S3 policy to your node IAM role:** ```bash NODE_ROLE_NAME="your-node-role" # e.g. eksctl-my-cluster-nodegroup-NodeInstanceRole-... BUCKET_NAME="your-bucket-name" ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) REGION="us-west-2" S3_POLICY_ARN=$(aws iam create-policy \ --policy-name zymtrace-s3-policy \ --policy-document "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":[\"s3:GetObject\",\"s3:PutObject\",\"s3:DeleteObject\",\"s3:ListBucket\",\"s3:GetBucketLocation\"],\"Resource\":[\"arn:aws:s3:::${BUCKET_NAME}\",\"arn:aws:s3:::${BUCKET_NAME}/*\"]}]}" \ --query "Policy.Arn" --output text) aws iam attach-role-policy --role-name $NODE_ROLE_NAME --policy-arn $S3_POLICY_ARN ``` **2. Increase the IMDS hop limit to 2** so pods can reach the metadata service. EKS nodes default to IMDSv2 with a hop limit of 1, which blocks pods from reaching IMDS. If you skip this step, pods will log: ``` WARN 🚧 [warn]: failed to load region from IMDS | err: failed to load IMDS session token: dispatch failure: timeout: HTTP read timeout ``` ```bash for id in $(aws ec2 describe-instances \ --region $REGION \ --filters "Name=tag:eks:cluster-name,Values=${CLUSTER_NAME}" \ "Name=instance-state-name,Values=running" \ --query "Reservations[*].Instances[*].InstanceId" \ --output text); do aws ec2 modify-instance-metadata-options \ --instance-id "$id" \ --http-put-response-hop-limit 2 \ --http-tokens required \ --region $REGION done ``` Applies immediately to running nodes, but will revert on node replacement. Also update the launch template to persist the setting. 1. Open **EC2** → **Instances** 2. Select all nodes in your node group 3. **Actions** → **Instance settings** → **Modify instance metadata options** 4. Set **Metadata response hop limit** to `2` 5. Click **Save** Persists the setting for all future nodes created by the node group. 1. Open **EC2** → **Launch Templates** 2. Find the launch template used by your EKS node group 3. **Actions** → **Modify template (Create new version)** 4. Under **Advanced details** → set **Metadata response hop limit** to `2` 5. Click **Create template version** 6. Open **EKS** → your cluster → **Compute** → select the node group → **Edit** 7. Update the launch template version to the new version 8. Roll the node group to apply (cordon + drain + replace nodes) **3. Set helm values:** ```yaml storage: mode: "use_existing" use_existing: type: "s3" s3: region: "us-west-2" useIAM: "aws" # no serviceAccount needed — credentials come from the node role buckets: symbols: "your-bucket-name" ``` :::note Node instance profile grants S3 access to **all pods on the node**, not just Zymtrace. Use IRSA if your cluster is shared with other workloads. ::: ##### Static Key Configuration
Helm values ```yaml storage: mode: "use_existing" use_existing: type: "s3" s3: region: "" accessKey: "" secretKey: "" buckets: symbols: "zymtrace-symdb" ``` **Required fields:** - `region`: AWS region where your S3 bucket is located (e.g., `us-west-2`) - `accessKey`: AWS access key ID - `secretKey`: AWS secret access key
##### IAM Permissions Your AWS user or role needs the following permissions for the specified bucket: ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::zymtrace-symdb", "arn:aws:s3:::zymtrace-symdb/*" ] } ] } ``` ##### Configuration Example ```yaml storage: mode: "use_existing" use_existing: type: "s3" s3: region: "us-west-2" accessKey: "AKIA..." secretKey: "..." buckets: symbols: "zymtrace-symdb-prod" ```
##### Bucket Creation ```bash aws s3 mb s3://zymtrace-symdb --region us-west-2 ``` ##### Testing Connectivity ```bash aws s3 ls s3://zymtrace-symdb --region us-west-2 ```
#### Google Cloud Storage Configuration [![Artifact Hub :zymtrace backend](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/zymtrace)](https://artifacthub.io/packages/helm/zymtrace/backend?modal=values)
Google Cloud Storage Configuration ```yaml storage: mode: "use_existing" use_existing: type: "gcs" gcs: endpoint: "https://storage.googleapis.com" # GCS endpoint, defaults to https://storage.googleapis.com accessKey: "" secretKey: "" buckets: symbols: "zymtrace-symdb" ``` **Required fields:** - `accessKey`: GCP service account access key - `secretKey`: GCP service account secret key
##### GCS Service Account Permissions Your GCP service account needs the following permissions for the specified bucket: - `storage.objects.create` - `storage.objects.delete` - `storage.objects.get` - `storage.objects.list` - `storage.buckets.get` You can assign the **Storage Object Admin** role for the specific bucket, or create a custom role with the minimal required permissions. ##### Bucket Creation ```bash gsutil mb -c STANDARD -l us-west1 gs://zymtrace-symdb ``` ##### Testing Connectivity ```bash gsutil ls gs://zymtrace-symdb ``` ##### Configuration Example ###### Production Environment ```yaml storage: mode: "use_existing" use_existing: type: "gcs" gcs: endpoint: "https://storage.googleapis.com" accessKey: "service-account@project.iam.gserviceaccount.com" secretKey: "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n" buckets: symbols: "zymtrace-symdb-prod" ```
## Bucket Configuration ### Bucket Names Zymtrace uses the following bucket for storing symbols: - **symbols**: Stores debugging symbols and related metadata (default: `zymtrace-symdb`) ### Bucket Creation **Automatic Creation**: If you have appropriate permissions, Zymtrace can automatically create the required bucket during initialization. **Manual Creation**: For production environments, create the bucket manually using your cloud provider's console or CLI tools. See the specific instructions in each storage provider tab above. ## Security Considerations ### Access Control - **Principle of Least Privilege**: Grant only the minimum required permissions - **Network Security**: Use HTTPS/TLS for connections to object storage - **Access Keys**: Use dedicated service accounts with limited scope ### Data Encryption - **Encryption in Transit**: Always use HTTPS for object storage connections - **Encryption at Rest**: Enable server-side encryption on your buckets - **Key Management**: Use managed encryption keys when possible ## Troubleshooting ### AWS S3 Authentication Modes Zymtrace supports two authentication modes for AWS S3: | Mode | How it works | When to use | |------|-------------|-------------| | **Static Keys** | Long-lived `accessKey` / `secretKey` set in helm values | Simple setups, non-EKS environments | | **IAM / IRSA** | `useIAM: "aws"` — credentials from EC2 node role (IMDS) or IRSA token | EKS deployments — IRSA is recommended | ### Common Issues 1. **Connection Timeout**: Check network connectivity and firewall rules 2. **Access Denied**: Verify credentials and permissions 3. **Bucket Not Found**: Ensure the bucket exists and is accessible 4. **SSL/TLS Errors**: Verify certificate configuration for HTTPS endpoints ### AWS: IMDS Timeout (failed to load region from IMDS) **Symptom:** Logs contain warnings like: ``` WARN 🚧 [warn]: failed to load region from IMDS | err: failed to load IMDS session token: dispatch failure: timeout: HTTP read timeout ``` **Cause:** This occurs when `useIAM: "aws"` is set and the pod tries to fetch credentials from the EC2 metadata service. EKS nodes default to IMDSv2 with a hop limit of 1, which blocks pods inside containers from reaching IMDS. **Fix A — IRSA (recommended):** Use IRSA so credentials are injected directly into the pod via a projected service account token — no IMDS required. See [IAM / IRSA setup](#irsa) above. **Fix B — Increase hop limit:** Increase the IMDSv2 hop limit to 2 on all nodes so pods can reach IMDS: ```bash for id in $(aws ec2 describe-instances \ --region \ --filters "Name=tag:eks:cluster-name,Values=" \ "Name=instance-state-name,Values=running" \ --query "Reservations[*].Instances[*].InstanceId" \ --output text); do aws ec2 modify-instance-metadata-options \ --instance-id "$id" \ --http-put-response-hop-limit 2 \ --http-tokens required \ --region done ``` Also update your node group **launch template** to persist this setting across node replacements. ### AWS: Access Denied with IAM Authentication **Symptom:** S3 operations fail with `AccessDenied` despite `useIAM: "aws"` being set. **Checklist:** - The IAM role has the S3 policy attached (`s3:GetObject`, `s3:PutObject`, `s3:DeleteObject`, `s3:ListBucket`) - For IRSA: the role trust policy matches the exact namespace and service account name - For IRSA: the service account has the `eks.amazonaws.com/role-arn` annotation - For IRSA: `storage.use_existing.s3.serviceAccount` is set to the annotated service account name - The bucket name in `storage.buckets.symbols` matches the bucket in the IAM policy resource ARN ### Next Steps After configuring object storage, proceed to configure the other storage components: [Configure ClickHouse](./clickhouse.md) | [Configure Postgres](./postgres.md)
### Deploy new MinIO instance This mode deploys and manages MinIO within your cluster.
MinIO Create Mode Configuration ```yaml storage: mode: "create" create: image: repository: minio/minio tag: "RELEASE.2024-12-18T13-15-44Z" config: user: "minio" password: "minio123" service: api: port: 9000 console: port: 9001 replicas: 1 resources: requests: cpu: "200m" memory: "512Mi" limits: cpu: "1000m" memory: "1Gi" storage: type: "persistent" size: 20Gi className: "" buckets: symbols: "zymtrace-symdb" ```
--- # postgres URL: https://docs.zymtrace.com/databases/postgres # Postgres Configuration ```mdx-code-block import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; ``` Postgres handles identity management and symbol database storage. It stores user authentication data and debugging symbol metadata. ## Configuration Modes [![Artifact Hub :zymtrace backend](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/zymtrace)](https://artifacthub.io/packages/helm/zymtrace/backend?modal=values) ### Connect to existing Postgres This mode connects to your existing Postgres database, AWS Aurora PostgreSQL or GCP Cloud SQL. #### Standard Postgres Configuration
Standard Postgres Configuration ```yaml postgres: mode: "use_existing" use_existing: host: "" # host:port user: "" password: "" database: "zymtrace" # Database name secure: false # Enable TLS/secure connection autoCreateDBs: false # When true, zymtrace migration will automatically create the required databases. # NOTE: For autoCreateDBs to work, the database user must have CREATEDB permission. # Grant with: ALTER USER "your-user" CREATEDB; ```
**Setting up the Postgres use_existing mode** Postgres setups can vary based on security needs like data classification and role usage. The example below is a simplified guideline. :::tip Note that unlike typical applications, which require only `INSERT` and `SELECT` permissions, the Zymtrace database migration job requires DDL access. If you're setting up Zymtrace for the first time, the most straightforward approach is to create one role with both DDL and DML permissions. ::: #### Database Setup Options You have two options for setting up the required databases: 1. **Manual Setup**: Create databases and users manually using SQL commands 2. **Automatic Setup**: Enable `autoCreateDBs` to let Zymtrace create databases during migration ##### Option 1: Manual Database Setup For manual database setup, use the SQL commands below. This approach gives you full control over user permissions and database ownership. The commands below assume you're connected to the database using a role with superuser privileges. ```sql CREATE ROLE zystem LOGIN PASSWORD 'metsyz'; CREATE DATABASE zymtrace_identity OWNER zystem; CREATE DATABASE zymtrace_symdb OWNER zystem; CREATE DATABASE zymtrace_web OWNER zystem; \c zymtrace_identity ALTER SCHEMA public OWNER TO zystem; \c zymtrace_symdb ALTER SCHEMA public OWNER TO zystem; \c zymtrace_web ALTER SCHEMA public OWNER TO zystem; ``` ##### Option 2: Automatic Database Setup Alternatively, you can enable `autoCreateDBs: true` in your configuration to let Zymtrace automatically create the required databases during migration. This requires the database user to have `CREATEDB` permission. **When to use automatic setup:** - Development environments where quick setup is preferred - When you have administrative access to grant broad CREATE permissions - Testing scenarios where database recreation is acceptable **When to use manual setup:** - Production environments requiring strict permission control - When following security best practices with minimal required permissions - Enterprise environments with database administration policies #### Security Considerations ##### Database Permissions Zymtrace requires both DDL (Data Definition Language) and DML (Data Manipulation Language) permissions: - **DDL permissions**: Required for database schema migrations - **DML permissions**: Required for normal application operations ##### User Role Management For production environments, consider creating separate roles for: - **Migration role**: Has DDL permissions for schema updates - **Application role**: Has minimal DML permissions for runtime operations #### Configuration Examples ##### Development Environment For development setups with simplified security: ```yaml postgres: mode: "use_existing" use_existing: host: "postgres.dev.company.com:5432" user: "zymtrace_dev" password: "dev-password" database: "zymtrace" secure: false autoCreateDBs: true ``` ##### Production Environment For production setups with enhanced security: ```yaml postgres: mode: "use_existing" use_existing: host: "postgres.prod.company.com:5432" user: "zymtrace_prod" password: "secure-prod-password" database: "zymtrace" secure: true autoCreateDBs: false # Manually create databases for production ```
#### AWS Aurora with IAM Authentication :::info RDS Compatibility These instructions should also apply to traditional AWS RDS PostgreSQL instances as it supports IAM authentication. :::
AWS Aurora IAM Authentication Configuration ```yaml postgres: mode: "use_existing" use_existing: host: "" # host:port user: "" # Database username for IAM authentication useIAM: "aws" # Enable AWS IAM authentication awsRegion: null # Override the default region when using IAM (optional) database: "zymtrace" # Database name prefix secure: true # Enable TLS/secure connection (required for IAM auth) autoCreateDBs: false # When true, zymtrace migration will automatically create the required databases. # NOTE: For autoCreateDBs to work, the database user must have CREATEDB permission. # Grant with: ALTER USER "your-user" CREATEDB; ```
**Prerequisites for AWS Aurora IAM Authentication:** 1. Enable IAM database authentication on your Aurora cluster 2. Create a database user for IAM authentication 3. Configure AWS IAM policies for database access 4. Set up AWS credentials or IAM roles for your Kubernetes cluster #### Setting up AWS Aurora IAM Authentication ##### Step 1: Enable IAM Database Authentication Enable IAM database authentication on your Aurora PostgreSQL cluster: ```bash # For Aurora cluster aws rds modify-db-cluster \ --db-cluster-identifier your-aurora-cluster \ --enable-iam-database-authentication ``` Or via the AWS Console: - Navigate to RDS → Databases → Your Aurora Cluster - Modify → Additional configuration → Database authentication - Enable "IAM database authentication" ##### Step 2: Create Database User and Databases Connect to your PostgreSQL database using the master user and create the IAM database user: ```sql -- Create IAM database user CREATE USER zymtrace_user LOGIN; GRANT rds_iam TO zymtrace_user; -- Create the three required databases CREATE DATABASE zymtrace_identity; CREATE DATABASE zymtrace_symdb; CREATE DATABASE zymtrace_web; -- Grant all privileges on databases to the IAM user GRANT ALL PRIVILEGES ON DATABASE zymtrace_identity TO zymtrace_user; GRANT ALL PRIVILEGES ON DATABASE zymtrace_symdb TO zymtrace_user; GRANT ALL PRIVILEGES ON DATABASE zymtrace_web TO zymtrace_user; ``` **Grant schema privileges** (connect to each database and run): ```sql -- For zymtrace_identity database \c zymtrace_identity GRANT ALL ON SCHEMA public TO zymtrace_user; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO zymtrace_user; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO zymtrace_user; -- For zymtrace_symdb database \c zymtrace_symdb GRANT ALL ON SCHEMA public TO zymtrace_user; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO zymtrace_user; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO zymtrace_user; -- For zymtrace_web database \c zymtrace_web GRANT ALL ON SCHEMA public TO zymtrace_user; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO zymtrace_user; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO zymtrace_user; ``` :::tip Database Permissions The database user needs ALL privileges on the schema for migrations to work properly. The `ALTER DEFAULT PRIVILEGES` commands ensure that future tables and sequences created by Zymtrace will have the correct permissions. ::: ##### Step 3: Configure IAM Policy Create an IAM policy that allows `rds-db:connect` action: ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "rds-db:connect" ], "Resource": [ "arn:aws:rds-db:REGION:ACCOUNT_ID:dbuser:CLUSTER_RESOURCE_ID/zymtrace_user" ] } ] } ``` **Finding your cluster resource ID:** ```bash aws rds describe-db-clusters \ --db-cluster-identifier your-aurora-cluster \ --query 'DBClusters[0].DbClusterResourceId' \ --output text ``` This outputs something like: `cluster-ABCDEFGHIJKL01234` **Complete example ARN:** ``` arn:aws:rds-db:us-west-2:123456789012:dbuser:cluster-ABCDEFGHIJKL01234/zymtrace_user ``` Create the policy: ```bash aws iam create-policy \ --policy-name ZymtraceAuroraConnectPolicy \ --policy-document file://aurora-connect-policy.json ``` ##### Step 4: Create IAM Role with OIDC Trust Policy **Option A: Using EKS Service Accounts (Recommended)** Create a trust policy for the IAM role. First, get your EKS cluster's OIDC provider: ```bash aws eks describe-cluster \ --name your-cluster-name \ --query "cluster.identity.oidc.issuer" \ --output text ``` This outputs something like: `https://oidc.eks.us-west-2.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE` Create a file named `trust-policy.json`: ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Federated": "arn:aws:iam::ACCOUNT_ID:oidc-provider/oidc.eks.REGION.amazonaws.com/id/OIDC_ID" }, "Action": "sts:AssumeRoleWithWebIdentity", "Condition": { "StringEquals": { "oidc.eks.REGION.amazonaws.com/id/OIDC_ID:sub": "system:serviceaccount:default:zymtrace-aurora-sa", "oidc.eks.REGION.amazonaws.com/id/OIDC_ID:aud": "sts.amazonaws.com" } } } ] } ``` Replace: - `ACCOUNT_ID` with your AWS account ID - `REGION` with your AWS region (e.g., `us-west-2`) - `OIDC_ID` with the ID from the OIDC issuer URL (the part after `/id/`) - `default` with your namespace if different Create the IAM role and attach the policy: ```bash # Create IAM role with trust policy aws iam create-role \ --role-name zymtrace-aurora-role \ --assume-role-policy-document file://trust-policy.json # Attach the Aurora connect policy aws iam attach-role-policy \ --role-name zymtrace-aurora-role \ --policy-arn arn:aws:iam::ACCOUNT_ID:policy/ZymtraceAuroraConnectPolicy # Create Kubernetes service account with IAM role annotation kubectl create serviceaccount zymtrace-aurora-sa kubectl annotate serviceaccount zymtrace-aurora-sa \ eks.amazonaws.com/role-arn=arn:aws:iam::ACCOUNT_ID:role/zymtrace-aurora-role ``` **Option B: Using EC2 Instance Profile** Attach the IAM policy to your EKS node group's instance profile. Note: This grants database access to all pods on the nodes. ##### Step 5: Configure Zymtrace Helm Values Create your Helm values file with the Aurora IAM authentication configuration: ```yaml postgres: mode: "use_existing" use_existing: host: "your-cluster.cluster-xxxxx.us-west-2.rds.amazonaws.com" # Aurora cluster endpoint user: "zymtrace_user" # The IAM database user created in Step 2 password: "" # Must be empty string when using IAM authentication useIAM: "aws" # Enable AWS IAM authentication awsRegion: "us-west-2" # Your AWS region database: "zymtrace" # Database name prefix secure: true # Required - IAM authentication requires TLS autoCreateDBs: false # Set to false if databases were created manually services: common: serviceAccountName: "zymtrace-aurora-sa" # The service account with IAM role annotation ``` :::warning Configuration Requirements - **password**: Must be `""` (empty string, not omitted) when using IAM authentication - **secure**: Must be `true` - IAM authentication requires TLS connection - **serviceAccountName**: Must match the service account created in Step 4 - **autoCreateDBs**: Set to `false` if you created databases manually in Step 2 ::: Deploy Zymtrace: ```bash helm install zymtrace-backend zymtrace/backend \ -f your-values.yaml \ --namespace default ``` #### Verification After deploying Zymtrace, verify the setup is working correctly: **1. Check service account annotation:** ```bash kubectl describe serviceaccount zymtrace-aurora-sa ``` Should show: ``` Annotations: eks.amazonaws.com/role-arn: arn:aws:iam::ACCOUNT_ID:role/zymtrace-aurora-role ``` **2. Check IAM authentication is enabled on Aurora:** ```bash aws rds describe-db-clusters \ --db-cluster-identifier your-cluster \ --query 'DBClusters[0].IAMDatabaseAuthenticationEnabled' ``` Should return: `true` **3. Check database migration logs:** ```bash kubectl logs -l app=db-migration ``` Should show successful database connection and migrations without password-related errors. **4. Test database connection from a pod:** ```bash kubectl run -it --rm postgres-test \ --image=postgres:16 \ --restart=Never \ --serviceaccount=zymtrace-aurora-sa \ -- psql "host=your-aurora-endpoint.rds.amazonaws.com port=5432 dbname=zymtrace_identity user=zymtrace_user sslmode=require" ``` #### Security Considerations - **TLS Connection**: IAM authentication requires a secure TLS connection (`secure: true`) - **Token Expiration**: IAM tokens have a 15-minute lifetime and are automatically refreshed - **Least Privilege**: Grant only the minimum required database permissions to IAM users - **Network Security**: Use VPC security groups to restrict database access #### Troubleshooting **Common Issues:** 1. **Connection Timeout** - Ensure Aurora security group allows port 5432 from your EKS cluster - Check VPC security group rules - Verify Aurora is in same VPC or network is configured correctly 2. **IAM Authentication Failed** - Verify the database user exists and has `rds_iam` role: ```sql SELECT usename, useconfig FROM pg_user WHERE usename = 'zymtrace_user'; ``` - Check IAM policy has correct cluster resource ID - Verify trust policy has correct OIDC provider URL 3. **"no password supplied" error** - Ensure `password: ""` in Helm values (empty string, not omitted) - Verify service account has IAM role annotation - Check pod is using the correct service account: ```bash kubectl get pod POD_NAME -o jsonpath='{.spec.serviceAccountName}' ``` 4. **"permission denied for schema public"** - Run the schema privilege commands from Step 2 - Ensure `ALTER DEFAULT PRIVILEGES` was executed for all three databases - Grant permissions manually: ```sql \c zymtrace_identity GRANT ALL ON SCHEMA public TO zymtrace_user; ``` 5. **SSL/TLS Errors** - Ensure `secure: true` is set in Helm configuration - IAM authentication requires TLS connection - Check Aurora has SSL/TLS enabled (default for Aurora) 6. **Token generation errors** - Verify OIDC provider is configured for your EKS cluster: ```bash aws iam list-open-id-connect-providers ``` - Check trust policy matches service account namespace - Ensure service account name in trust policy matches actual service account 7. **Database does not exist errors** - Verify all three databases were created: `zymtrace_identity`, `zymtrace_symdb`, `zymtrace_web` - Check database names in Aurora: ```bash psql -c "\l" -h your-endpoint -U postgres ``` 8. **Migration job fails** - Check if user has sufficient privileges on all schemas - Verify `ALTER DEFAULT PRIVILEGES` was executed - Check migration job logs: ```bash kubectl logs -l app=db-migration --tail=100 ``` #### Quick Reference **Complete setup checklist:** - [ ] IAM database authentication enabled on Aurora cluster - [ ] Database user `zymtrace_user` created with `rds_iam` role - [ ] Three databases created: `zymtrace_identity`, `zymtrace_symdb`, `zymtrace_web` - [ ] Schema privileges granted with `ALTER DEFAULT PRIVILEGES` - [ ] IAM policy created with `rds-db:connect` permission - [ ] IAM role created with OIDC trust policy - [ ] Kubernetes service account created and annotated with IAM role ARN - [ ] Helm values configured with `password: ""`, `secure: true`, and correct `serviceAccountName` - [ ] Security groups allow port 5432 from EKS cluster **Key configuration values:** ```yaml postgres: mode: "use_existing" use_existing: host: "your-cluster.cluster-xxxxx.REGION.rds.amazonaws.com" user: "zymtrace_user" password: "" # Empty for IAM auth useIAM: "aws" awsRegion: "REGION" database: "zymtrace" secure: true # Required autoCreateDBs: false services: common: serviceAccountName: "zymtrace-aurora-sa" ```
#### GCP Cloud SQL Configuration
GCP Cloud SQL Configuration ```yaml postgres: mode: "gcp_cloudsql" gcp_cloudsql: instance: "" # PROJECT:REGION:INSTANCE format, e.g. zymtrace-cloudsql-psql-1 user: "" # IAM account, e.g zt-db-762@local-bebop-448118-g4.iam (without gserviceaccount.com suffix) database: "zymtrace" # Database prefix for zymtrace_identity, zymtrace_profiling, and zymtrace_web databases autoCreateDBs: false # When true, zymtrace migration will automatically create the required databases. # NOTE: For autoCreateDBs to work, the IAM database user must have CREATEDB permission. # Grant with: ALTER USER "zt-db-762@local-bebop-448118-g4.iam" CREATEDB; workloadIdentity: enabled: true # Enable Workload Identity for authentication proxy: image: repository: gcr.io/cloud-sql-connectors/cloud-sql-proxy tag: "2.15.0" # Use nodeSelector if you created a dedicated node pool with cloud-platform scope nodeSelector: cloud.google.com/gke-nodepool: cloudsql-pool resources: requests: cpu: "100m" memory: "128Mi" limits: cpu: "500m" memory: "556Mi" port: 5432 serviceAccount: "zymtrace-cloudsql-sa" # Kubernetes service account bound to GCP service account via Workload Identity ```
**Prerequisites for GCP Cloud SQL:** 1. Set up Workload Identity between your Kubernetes service account and GCP service account 2. Grant the GCP service account Cloud SQL Client role 3. Ensure the IAM database user has appropriate permissions in your Cloud SQL instance For detailed setup instructions including creating the Cloud SQL instance, configuring Workload Identity, and setting up IAM authentication, refer to the [GCP Cloud SQL setup guide](gcp-cloudsql.md).
### AWS Aurora PostgreSQL This mode provides native support for AWS Aurora PostgreSQL with IAM authentication.
AWS Aurora Configuration ```yaml postgres: mode: "aws_aurora" aws_aurora: host: "" # Aurora cluster endpoint user: "" # Database user for IAM authentication database: "zymtrace" # Database prefix region: "" # AWS region autoCreateDBs: false serviceAccount: "" # Kubernetes service account with IAM role ```
**Prerequisites:** 1. AWS EKS cluster with OIDC provider configured 2. Aurora PostgreSQL cluster with IAM authentication enabled 3. IAM role with RDS connect permissions 4. Kubernetes service account annotated with IAM role ARN For detailed setup instructions including creating the Aurora cluster, configuring IAM authentication, and setting up OIDC integration, refer to the [AWS Aurora setup guide](aws-aurora.md)
### GCP Cloud SQL PostgreSQL This mode provides native support for GCP Cloud SQL PostgreSQL with Workload Identity authentication.
GCP Cloud SQL Configuration ```yaml postgres: mode: "gcp_cloudsql" gcp_cloudsql: instance: "" # PROJECT:REGION:INSTANCE format user: "" # IAM account (without gserviceaccount.com suffix) database: "zymtrace" # Database prefix autoCreateDBs: false workloadIdentity: enabled: true proxy: image: repository: gcr.io/cloud-sql-connectors/cloud-sql-proxy tag: "2.15.0" resources: requests: cpu: "100m" memory: "128Mi" limits: cpu: "500m" memory: "556Mi" port: 5432 serviceAccount: "zymtrace-cloudsql-sa" ```
For detailed setup instructions, refer to the [GCP Cloud SQL setup guide](gcp-cloudsql.md).
### Deploy new Postgres instance This mode deploys and manages Postgres within your cluster.
Postgres Create Mode Configuration ```yaml postgres: mode: "create" create: config: user: "postgres" password: "postgres123" service: port: 5432 resources: requests: cpu: "200m" memory: "512Mi" limits: cpu: "1000m" memory: "1024Mi" storage: type: "persistent" size: 20Gi className: "" ```
--- # Allocation Profiling URL: https://docs.zymtrace.com/features/allocation-profiling # Allocation Profiling Zymtrace can sample memory allocations alongside CPU profiles, letting you attribute heap pressure and GC overhead back to the call paths that produced it. **Why Zymtrace's approach is different:** - **No library injection** — we don't load any shared library into the target JVM. Your application's process image is untouched. - **Low overhead** — sample-based, interval-driven collection (e.g. one sample per `16mib` allocated) keeps the cost down for steady-state production workloads. - **On-demand, time-limited** — turn it on when you need it, off (or auto-expired) when you don't. Rules carry an expiry so allocation profiling never runs longer than you intended. - **Scoped via rules** — target a specific container, deployment, host, or project with a CEL expression. No global toggles, no agent restarts. :::info Language support Allocation Profiles is currently only supported for **Java** (OpenJDK, Zulu, and Zing). Need it for Python or another language? Reach out on [Slack](https://join.slack.com/t/zymtrace/shared_invite/zt-3fdidjufl-q~NHxDzQlzal2B9mujfaoQ) or email [support@zymtrace.com](mailto:support@zymtrace.com). :::
Allocation Profiles view with flamegraph, call tree, top hotspots, and AI Assistant recommendations
The Allocation Profiles view shows a memory-allocation flamegraph, a per-thread breakdown, and (when enabled) an AI Assistant pass that summarises the hottest allocation paths and suggests concrete fixes. ## How it works Zymtrace uses a custom sampling method built on what is backing JVMTI. This gives us the benefits of low-overhead JVMTI allocation sampling without the drawbacks — namely, we don't have to load any shared libraries into the target process. Supported for **OpenJDK**, **Zulu**, and **Zing**; the JVMTI-backed path is used by default and can be disabled with `-use-jvmti=false`. Sampling is interval-based: you specify a size (e.g. `16mib`) and Zymtrace records one allocation sample per that many bytes allocated. Smaller intervals give finer detail at higher overhead; larger intervals are cheaper and suitable for steady-state production workloads. ## Enabling allocation profiling There are two ways to turn allocation profiling on. **We recommend the UI** — it lets you toggle it on-demand and scope the configuration to specific hosts or processes without redeploying the agent. ### Option 1 (recommended): from the UI In the UI, allocation profiling is enabled via an **agent rule** — a CEL expression that targets specific containers, deployments, hosts, or projects, with the sampling rate and JVMTI flag attached. Rules are evaluated top-to-bottom, take effect on the next profiler reporter interval, and don't require an agent restart. There are two ways to create an agent rule: **Agent Config page** — go to **Settings → Agent Config**. You'll see the existing rules and can click **+ Add Rule** to author one directly with a CEL expression and configuration settings.
Agent Config page showing allocation profiling rules
**Shortcut from the Allocation Profiles page** — open the **Allocation Profiles** page, add filters for the specific app, container, or deployment you care about, and click the **Create Agent Rule** shortcut. The filters are translated into the CEL expression for you, so you only have to pick the sampling rate.
Filter bar with the Create Agent Rule shortcut
### Option 2: via profiler configuration Pass `-alloc-profile ` (or set `ZYMTRACE_ALLOC_PROFILE=`) on the profiler at startup. This applies globally to whatever the agent profiles and requires redeploying the DaemonSet / restarting the binary to change. ```bash sudo ./zymtrace-profiler \ -collection-agent=gateway.company.com:443 \ -alloc-profile=16mib ``` Common values: - `-alloc-profile=512kib` — fine-grained sampling, higher overhead - `-alloc-profile=16mib` — typical default for production workloads | CLI Argument | Environment Variable | Description | |--------------|---------------------|-------------| | `-alloc-profile` | `ZYMTRACE_ALLOC_PROFILE` | Enable allocation profiling. Takes a sampling interval size, e.g., `-alloc-profile 16mib`. Default is `""` (disabled). | | `-use-jvmti` | `ZYMTRACE_USE_JVMTI` | Control whether JVMTI is used as the sampling backend. Default is `true` (when supported). Set to `false` to disable. | ## Related - [Profiler ENV and CLI Args](../install/profiler/profiler-configuration.md) — full profiler configuration reference - [Supported Languages](../install/requirements/supported-languages.md): current Java version coverage --- # Diff Functions URL: https://docs.zymtrace.com/features/diff # Diff Functions The **Diff Functions View** is designed to provide a precise, function-level comparison of compute usage between two distinct states. It helps you measure performance shifts, identifying both improvements and regressions across different releases, A/B tests, or deployments. ## What Can You Do With Diff Functions View? When you change your code or deployment configuration, the Diff View is the fastest way to validate its performance impact. It's perfect for detecting performance regressions that might be hidden when a fix in one area shifts a bottleneck to another. Popular use-cases include: - **Compare Releases:** Instantly see resource consumption differences between two versions of your application (e.g., `v1` vs. `v2`). - **A/B Testing:** Quantify the resource cost of a new feature by comparing performance between test groups (`Control` vs. `Experiment`). - **Blue/Green Deployments:** Track performance shifts when switching traffic between environments to ensure the new deployment is stable and efficient. - **Code Change Validation:** Measure the exact impact of a specific commit, library update, or configuration change.
![Diff functions](../../static/img/features/diff-functions.png) *Diff Functions*
## How to Use the Diff Functions View The view operates by defining a **Baseline** and a **Comparison** scope, presenting the difference in resource usage function by function. 1. **Define Scopes:** * **Baseline:** Use the left selector (e.g., `container.name: clock-slow` and `Last 1 Hour`) to define the initial state, typically the "before" or older version. * **Comparison:** Use the right selector (e.g., `container.name: clock-fast` and `Last 1 Hour`) to define the new state, typically the "after" or current version. * *Tip:* You can compare time ranges (e.g., "Yesterday" vs. "Today"), or a combination of time and filterable entities (e.g., two different containers, like in the screenshot). 2. **Analyze the Table:** The table lists all functions whose resource usage changed between the Baseline and the Comparison. * **Search and Filter:** Use the `get` search bar to find a specific function or file path (e.g., search for a utility function or a known component). * **Drill Down:** Click any function to see detailed metrics, flame graphs, and historical trends for that specific function in both the Baseline and Comparison scopes. ## Understanding the Data Columns The table focuses on the change in "Cores Used" to measure resource consumption, showing both the absolute and percentage change. | Column | Description | | :--- | :--- | | **Function** | The name of the function or file path where the resource usage occurred. | | **Cores Used (self)** | The difference in CPU time **spent directly** inside this function, excluding its children. | | **Cores Used (total)** | The difference in CPU time **spent in this function and all its children** (the entire sub-tree of calls). This is usually the best metric for total impact. | | **Trend** | A link to the historical trend view for the function, allowing you to see the change over a longer timeline. | ### Interpreting the Color Codes The color of the percentage change indicates the nature of the performance shift: | Color | Meaning | Description | | :--- | :--- | :--- | | **Green** | **Improvement** (Negative Change) | Indicates a **reduction** in resource usage (e.g., CPU consumption is lower). These are positive changes, showing the function is more efficient in the Comparison scope. | | **Red** | **Regression** (Positive Change) | Indicates an **increase** in resource usage. These are negative changes, showing the function is consuming more resources. **Investigate these entries.** | | **Purple** | **New/Added** | Marks functions that appeared in the Comparison scope but did not exist in the Baseline. | | **Removed** | **Removed** | Marks functions that existed in the Baseline but are **no longer present** in the Comparison scope, meaning the function was completely removed in the comparison and its baseline usage is now gone/shifted). | | **Unchanged** | No Significant Change | Functions with no significant change in resource usage are hidden by default, but can be displayed if filtering is applied. | ## Drilling Down to the Trend (Diff Trend Analysis) Clicking a function opens the **Diff Function Trend** panel, which provides a time-series comparison of the function's resource consumption across the selected Baseline and Comparison time ranges.
![Diff functions](../../static/img/features/diff-functions-trend.png) *Diff Functions Trend Analysis*
### Key Data Points * **Function and File:** Clearly identifies the function (e.g., `get_time`) and its location (`/app/clock.py`). * **Total Time / Self Time:** Displays the overall percentage change for both the time spent in the function *and its children* (**Total Time**), and the time spent *only in the function itself* (**Self Time**). * **Absolute Change:** Provides the resource consumption averages for both scopes and the absolute difference. * **Baseline:** The average usage for the Baseline period (e.g., **16.01 mc**). * **Comparison:** The average usage for the Comparison period (e.g., **8.54 mc**). * **Change:** The absolute difference (e.g., **-7.47 mc**) and percentage change (e.g., **-46.66%**). This visualization allows you to confirm if the observed performance shift is **consistent** throughout the entire period or if it's the result of a few large spikes. It also provides a clear visual confirmation of the magnitude of the improvement or regression. --- # Efficiency IQ URL: https://docs.zymtrace.com/features/efficiency-iq # Efficiency IQ The Efficiency IQ dashboard is your main entry point into Zymtrace. It provides a high-level overview of your system's performance, combining compute utilization, top functions, and top entities in a single view. Efficiency IQ helps you quickly understand: * Overall compute utilization across your entire fleet * How much compute is actively used versus sitting idle * Top 5 most expensive functions * Top 5 entities - broken down by Hosts, Deployments, Containers, and Main Executables ### Absolute vs Relative CPU Mode You can view CPU utilization in **absolute** or **relative** mode: ![CPU Mode Toggle](../../static/img/features/abs-cpu.png) **Absolute mode** shows real CPU usage percentages — just like what you'd see in tools such as `htop` or `btop`. For example, if your cluster has 2 machines with 8 cores each, and your profiled service uses 4 cores on each machine, Efficiency IQ will display 50% CPU usage for that service. **Relative mode** presents CPU utilization in relation to other workloads you're profiling, helping you see which services dominate your compute resources. ### GPU & CPU Toggles Zymtrace profiles both CPU and GPU. You may toggle between CPU and GPU tabs on the efficiency IQ page and on most pages. All GPU profiling times are currently relative.
![Efficiency IQ Dashboard](../../static/img/features/eiq.png) *Zymtrace efficiency IQ*
--- # Filtering Data URL: https://docs.zymtrace.com/features/filtering-data # Filtering Data Zymtrace supports global filters across nearly all pages. Use filters to slice and dice your data efficiently. Zymtrace is fully OpenTelemetry (OTel) compliant. For all attributes that are defined in the [OTel semantic convention](https://opentelemetry.io/docs/specs/semconv/), we use the standardized attribute names. ## Two Filtering Modes ### 1. Click and Apply Filters Click on any filter category (Pod Name, Deployment Name, Thread Name, Container Name, etc.) to see available options. Each entity shows its compute time consumption as a percentage relative to workloads you are profiling across your entire infrastructure. This enables you to quickly identify the expensive resources, helping you prioritize optimization efforts that deliver quick wins. ![Filter Selection](../../static/img/features/filter-select.png) Simply click an entity to apply it as a filter, or enter a custom value to filter by name pattern. ### 2. Query Mode Enter a full query for advanced filtering. Zymtrace supports **[CEL (Common Expression Language)](https://cel.dev/)** syntax, and all entities use **OTEL** resource attributes in the query. #### Filter Attributes | Attribute | Category | Description | |---------------------------|------------|------------------------------------------------------------------------------------| | `k8s.pod.name` | Kubernetes | Pod name | | `k8s.pod.label` | Kubernetes | Kubernetes pod labels as `key:value` pairs | | `k8s.deployment.name` | Kubernetes | Deployment name | | `k8s.namespace.name` | Kubernetes | Kubernetes namespace | | `container.name` | Kubernetes | Container name | | `host.name` | Host | Host machine name | | `host.arch` | Host | Host architecture (e.g. `amd64`, `arm64`) | | `gpu.name` | GPU | GPU model name (e.g. `NVIDIA H100 80GB HBM3`) | | `gpu.uuid` | GPU | GPU unique identifier | | `process.executable.name` | Process | Main executable being profiled | | `thread.name` | Process | Thread name | | `profile.frame.type` | Process | Stack frame type (e.g. `cuda`, `jvm`, `dotnet`, `kernel`, `ruby`, `cpython`) | | `script.name` | Process | Script name for interpreted languages (e.g. `user/foo.py`). Currently Python only. | | `cluster.name` | Zymtrace | Cluster name set via `-cluster-name` on the profiler | | `user.tag` | Zymtrace | Custom `key:value` tags set via `-tags` on the profiler | | `job.tag` | Zymtrace | User-defined env var used as the custom job tag | | `slurm.job.id` | SLURM | SLURM job ID | | `slurm.job.name` | SLURM | SLURM job name | | `slurm.job.user` | SLURM | Name of the user executing the SLURM job | #### Pattern Matching with `matches()` Use the `matches()` function for regex and wildcard pattern matching: ```cel matches(container.name, "vllm.*") // matches containers starting with "vllm" ``` ```cel matches(k8s.pod.name, ".*backend.*") // matches pods containing "backend" ``` ```cel matches(process.executable.name, "python.*") // matches python executables ``` #### Example Queries **Example queries:** ```cel k8s.namespace.name == "production" ``` ```cel host.name == "node-1" || host.name == "node-2" ``` ```cel process.executable.name == "python3" && profile.frame.type == "python" ``` ```cel container.name == "backend" && k8s.namespace.name != "staging" ``` For `k8s.pod.label`, use key access to filter by a specific label: ```cel k8s.pod.label["app"] == "nginx" ``` ![Filter Selection](../../static/img/features/filter-query.png) ## Script Name Filtering You can also filter by script name in Zymtrace. Python and other script language applications share the same main executable name, making them difficult to differentiate and filter. Multiple Python workloads would all simply show up as python. To solve this, we directly collect the Python script name, enabling you to drill down to the specific application. This feature is currently limited to Python and we plan to extend support to Java and other high-level languages. Contact us if you need this in other languages. ![Script Name Filter](../../static/img/features/script-name-filter.webp) ## SLURM Job Filtering On SLURM clusters, Zymtrace automatically tags every profile and metric with the job's ID, name, and submitting user (`slurm.job.id`, `slurm.job.name`, `slurm.job.user`), letting you slice your entire fleet by SLURM job. This requires SLURM to be configured with cgroup/v2 process tracking. See [SLURM-Aware GPU Profiling](/gpu/slurm) for how it works and how to enable it. For example, to focus on all of a user's jobs: ```cel slurm.job.user == "alice" ``` Or one user's GPU work within a specific job: ```cel slurm.job.id == "1234" && profile.frame.type == "cuda" ``` Filtering profiles by SLURM job, name, and user in Zymtrace ## Filter Scope Filters apply globally across, including: - Efficiency IQ - Top Functions - Top Entities - CPU Profiles - GPU Profiles Once applied, filters persist as you navigate between different views, allowing you to maintain focus on specific entities throughout your analysis. --- # Using Zymtrace Flamegraphs URL: https://docs.zymtrace.com/features/flamegraphs/flamegraph-controls import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; # Using the Flamegraph The **Zymtrace** flamegraph is a *mixed-stack* flamegraph, meaning it visualizes call stacks across multiple runtimes. This power comes with complexity — the volume of data can be overwhelming — so we've designed controls to help reduce noise and clutter. To analyze performance more effectively, use the [filters](../filtering-data.md) to narrow your view to specific workloads or areas of interest. ## Difference between GPU and CPU Flamegraphs The main difference between CPU and GPU flamegraphs is the introduction of CUDA-specific frames: `CUDA Instruction Offset`, `CUDA Kernel`, `CUDA Kernel Launch`, `CUDA SASS Mnemonic`, and `CUDA Stall Reason`. Additionally, the measurement units differ—GPU flamegraphs use GPU time, while CPU flamegraphs use CPU time (both relative time and absolute CPU core usage). Refer to the [Unit Reference](../unit-reference.md) for detailed explanations. All other controls and features remain the same across both flamegraph types.
The Zymtrace [CPU profiler](../../install/profiler/install-profiler.md) unwinds stacks across the full execution path, from high-level languages through user space to the kernel. ![CPU Flamegraph](../../../static/img/features/fg-cpu-annotation.png) *CPU flamegraph showing function call stacks and CPU time consumption*
Refer to [GPU Profiler architecture](../../gpu-profiler-architecture.md) to learn how it works.
![GPU Flamegraph](../../../static/img/features/fg-gpu.png) *GPU flamegraph showing GPU kernel execution and memory operations*
## Interactive Controls The flamegraph is fully interactive. Here are some of the controls we've built in. These controls are documented in the **"How do I use this?"** button in the bottom-right corner of the GPU and CPU flamegraph pages. | Action | Control | Description | | :-------------------- | :------------------------- | :------------------------------------------------- | | **Pan** | Drag with mouse or minimap | Move the entire graph view. | | **Pan Horizontally** | `Shift` + Mouse Wheel | Scroll left or right along the X-axis. | | **Pan Vertically** | Mouse Wheel | Scroll up or down through the call stack (Y-axis). | | **Zoom** | `Cmd`/`Ctrl` + Mouse Wheel | Zoom in or out on the focused area. | | **Reset Zoom** | Left-click empty space | Return to the initial unzoomed state. | | **Focus on Function** | Left-click a node | Focus on a specific function. Clicking on a focused function also resets the zoom | | **Analyze Function** | Right-click a node | Open the context menu for detailed analysis. | ## Key Components ### Flamegraph Settings Flamegraph Settings controls how stack traces are displayed in the flamegraph and [top functions](../top-functions.md) views (sandwich view). These settings allow you to balance detail versus simplicity by collapsing repetitive frames, cleaning up function names, and filtering out noise. By default, the flamegraph is simplified: successive unsymbolized frames and recursive calls are collapsed, libc functions show their common names (e.g., "write" instead of "__GI___libc_write"), etc. You can toggle these options to see more granular details when debugging specific issues, such as viewing frame addresses, enabling verbose function names, or separating collapsed frames.
![GPU Flamegraph](../../../static/img/features/fg-settings.png) *Flamegraph settings*
### CPU % Mode — Absolute vs. Relative The **CPU % Mode** toggle switches between two visualization modes. * **Abs (Absolute)** — Displays real CPU usage percentages, similar to `btop`. * **Rel (Relative)** — Displays CPU usage relative to other profiled workloads. See [Unit Reference](../unit-reference.md) for additional details. :::info The CPU % Mode toggle is not available on GPU profile flamegraphs because GPU profiles use relative times. ::: ### Search Use the **search bar** to quickly locate functions of interest. * **Basic search** — Type a function name directly, for example: ``` calculateDiffusion ``` * **Regex search** — Use regular expressions for pattern-based matching: ``` ^con.*Call$ // matches functions starting with "con" and ending with "Call" java.*Thread // matches Java thread-related functions .*numpy.* // matches NumPy library functions ``` ### Group By The **Group By** dropdown organizes flamegraph data along different dimensions: * **Main Executable** (default) — Groups by the primary process executable. * **Script Name** — Groups by application script name (currently supported for Python). * **Host** — Groups by individual machines. * **Thread** — Groups by process threads.
![GPU Flamegraph](../../../static/img/features/group-by.png) *Grouping flamegraph*
### Mirror Section (Mini-map) The **mirror section** shows a miniature overview of the entire flamegraph. Click and drag within it to navigate quickly to different regions of the graph. ### Interactive Legend The **legend** at the bottom shows color mappings for frame types (languages or runtimes). The legend is interactive — click a frame type to hide or show it, helping reduce visual noise.
![Flamegraph Legend](../../../static/img/features/fg-legend.png) *Flamegraph legend*
--- # Intro to Flamegraphs URL: https://docs.zymtrace.com/features/flamegraphs/ # Introduction to Flamegraphs A **flamegraph** is one of the most effective ways to understand how your application uses compute resources across CPUs and GPUs. It shows where your code spends time, why CPUs are busy, and why GPUs might be underutilized. When we profile your application through sampling, we can generate thousands of lines of data that reveal which code paths are hot (i.e., busy on-CPU). Flamegraphs condense this data into a single, interactive visualization, making it easy to identify which parts of your application consume the most compute and how that usage flows through your call stack. This visualization helps you quickly identify bottlenecks and inefficiencies across your entire system — turning what would be an overwhelming list of stack traces into an immediately comprehensible picture of your code's behavior. In this tutorial, we'll focus on **CPU flamegraphs**, which show how your program uses the CPU over time. While **Zymtrace** also supports GPU profiling, we covered the differences between CPU and GPU flamegraphs [here](/features/flamegraphs/flamegraph-controls#difference-between-gpu-and-cpu-flamegraphs). For now, let's learn how to spot performance bottlenecks using flamegraphs. Before diving into how to read a flamegraph, let's review how **Zymtrace** collects and organizes profiling data.
![Flamegraph example](../../../static/img/features/fg-dummy.png) *An illustration of a flamegraph*
## From Stack Traces to Flamegraph A stack trace is a snapshot of the call stack at a specific moment, capturing the chain of function calls from your application's entry point down to the currently executing code. Think of it as a breadcrumb trail showing exactly how your program arrived at a particular line. Stack traces are the foundation of profiling; they reveal what your application is executing and, crucially, help you trace back the steps that led to high resource usage. Profiling platforms rely on stack traces to pinpoint which code is running when your servers are consuming high CPU. Tools like `top` show which processes use the most CPU, but lack the detail to identify the exact lines of code responsible. To provide code-level insight, **Zymtrace** samples every busy process at `19Hz` by default, using `eBPF` to profile not only your code but also third-party libraries and kernel execution. This whole-system approach means you see everything that contributes to resource usage. However, interpreting millions of stack traces across your fleet is challenging. This is the problem flamegraphs were invented to solve. Let's look at a simple example. Imagine an application modeling thermal diffusion. After profiling, Zymtrace might collect results similar to the function call stacks shown below. | Percentage | Function Calls | | :--- | :--- | | **57%** | `startSimulation → setupGrid → calculateDiffusion` | | **31%** | `startSimulation → loadInitialState → readBoundaryConditions` | | **26%** | `startSimulation → setupGrid → calculateDiffusion → updateTemp` | | **15%** | `startSimulation → setupGrid → calculateDiffusion → updateTemp → libpython.so` | | **9%** | `startSimulation → setupGrid → calculateDiffusion → updateTemp → libpython.so → syscall` | From this table, you can see that the deep call sequence through `calculateDiffusion → updateTemp → libpython.so` is predominant, indicating that the main performance bottleneck lies in the `calculateDiffusion` function. While this breakdown works for small examples, production systems generate *millions* of unique stack traces across many processes, libraries, and kernel calls. A **flamegraph** condenses all that data into a single, readable view, making it possible to find bottlenecks at a glance. ## Following the Shape of the Graph **Zymtrace** uses an **Icicle Graph**, an inverted version of the classic flamegraph. The principles are the same — only the orientation changes. The key elements to look out for are: 1. **Rectangles** — Each rectangle (or "frame") represents a single function call. 2. **Hierarchy (Y-axis)** — The vertical position shows the call stack depth. The **root function** appears at the top, and its **children** are drawn directly below. 3. **Width (X-axis)** — The width of each rectangle represents how much total CPU time that function and its descendants consume. **Wider means costlier.** :::tip Note that the x-axis does not represent time; Zymtrace uses left-heavy sorting, where the most expensive child functions appear on the left side of their parent. This design makes hotspots immediately visible. ::: In more practical terms, use the following rules and actions: ### Width: Finding the Hot Spots **Rule:** The wider the rectangle, the more total CPU time that function (and its children) consumes. **Action:** Scan the graph left to right. The widest blocks are your *hotspots* — functions or code paths that take up most of the CPU time. Start your investigation there. ### Hierarchy: Tracing the Source **Rule:** Functions below a parent consume part of the parent's time. **Action:** When you see a wide parent function whose width is dominated by a single child, the child is the *true* source of cost. For example, if `calculateDiffusion` is wide because of calls through `updateTemp`, that deeper call path is your real optimization target. This vertical inspection helps you separate *who calls what* and *where the time actually goes*. ### Height: Spotting Complexity **Rule:** Tall stacks mean deep call hierarchies — often a sign of high abstraction or recursion. **Action:** Deep stacks aren't always bad, but they can signal unnecessary complexity. Consider simplifying overly nested code paths or flattening heavy recursive calls. ## Searching and Navigating in Zymtrace Large flamegraphs can be overwhelming, so **Zymtrace** includes search and navigation features: 1. Use the **Search bar** 2. Enter a function name or regex pattern. 3. The graph will be filtered to remove any stack traces that don't match the search value and the functions are highlighted. Refer to the next section for details. ## A Simple Optimization Workflow In summary, here's a repeatable process for using Zymtrace flamegraphs effectively: 1. **Profile continuously** — Zymtrace automatically captures stack data across your system. 2. **Scan horizontally** — look for wide functions; these are your CPU hotspots. 3. **Drill down vertically** — trace the cost to the child functions causing it. 4. **Check depth** — note deep or complex stacks that may benefit from simplification. 5. **Optimize and measure** — refactor, then use Zymtrace again to confirm improvements. --- # GPU Topology URL: https://docs.zymtrace.com/features/gpu-topology # GPU Topology GPU Topology shows how the GPUs on a host are connected to each other and to the CPUs, together with their health and utilization. It flags hardware and placement problems that slow workloads down, such as degraded NVLink connections or processes running on the wrong NUMA node, and recommends a fix. GPU Topology for an 8x NVIDIA B300 host: host summary with degraded NVLink pairs, warp stall distribution, interconnect matrix colored by NUMA node, and NUMA affinity recommendations ## Host summary Choose a host from the picker next to the title. The list shows each GPU host with its GPU model, GPU memory, and cluster. The summary shows the GPUs, the number of NVLink pairs and how many are degraded, the CPU, memory, kernel version, and the host's tags. ## Warp Stall Distribution This bar shows the share of sampled GPU time spent in each stall reason across all GPUs on the host, such as memory, compute, or synchronization. ## Interconnect Matrix The matrix shows the connection between every pair of GPUs, using `nvidia-smi topo -m` notation: `NV1` to `NV18` for the number of bonded NVLinks, or the PCIe path `PIX`, `PXB`, `PHB`, `NODE`, or `SYS` when there's no NVLink. GPU labels are colored by NUMA node, and degraded links are marked. Hover over or click a cell to see the link's lanes, TX and RX throughput, speed, and error rates: Flit CRC errors, replay errors, and link recoveries. ### Reading the matrix - `NV18` means the pair has 18 bonded NVLinks, not that they're healthy. Check the cell's error rates and throughput. - One degraded pair usually points to a single link. Degraded links across one GPU's whole row and column point to that GPU's port, cable, or switch path. - A clean matrix with slow collectives points back to the workload. Check its profile. - A `SYS` path or rising link errors point to the node. Fix the hardware before tuning code. ## Recommendations Zymtrace lists topology problems found on the host, each with a fix: - **NUMA placement:** when a process runs on CPUs that aren't on the same NUMA node as its GPU, Zymtrace shows which CPUs it used and how to pin it to the right node with `numactl`. - **Interconnect:** when a link degrades or a GPU pair falls back to PCIe, Zymtrace names the affected GPU and its peers, and the next step, such as draining the node, reseating a link, or rechecking with `nvidia-smi topo -m`. ## NUMA Topology & GPU Details GPUs are grouped by NUMA node, with the CPU threads and memory of each node. Click a GPU to expand it: - **Device metadata:** UUID, framebuffer, NUMA node, NUMA-affine CPU threads, and NVLink lanes and version. - **Live metrics:** utilization, memory used, power, temperature, and SM and memory clocks. - **Workload bottlenecks:** stall reasons for the GPU's top kernels. - **Device health:** ECC errors, retired pages, row remapping, XID errors, and PCIe error counters. Expanded GPU with device metadata, live metrics, workload bottlenecks per kernel, and ECC errors under device health Each GPU links to its CUDA flamegraph, CUDA top functions, and GPU metrics. Use **More** for the host's health metrics, CPU flamegraph, and CPU and CUDA top functions. For background and examples, see the blog post [GPU Topology Health: Zymtrace Adds the Missing Hardware Layer](https://zymtrace.com/article/gpu-topology-health/). --- # Overview URL: https://docs.zymtrace.com/features/ # Tutorial Overview Welcome to Zymtrace! This guide covers all the features and capabilities that help you analyze performance, identify bottlenecks, and optimize your applications using continuous profiling data. ## Core Analysis Views Zymtrace provides five main views for analyzing your application performance: ### [Efficiency IQ](efficiency-iq.md) - Your Performance Dashboard Your main entry point showing: - Overall compute utilization across your entire fleet - Top 5 most expensive functions consuming CPU/GPU resources - Top 5 entities (hosts, deployments, containers) by resource consumption - Absolute vs relative CPU usage modes for infrastructure and workload analysis **Perfect for:** Getting a quick system overview and identifying what needs attention first. ### [Top Functions](top-functions.md) - Function-Level Analysis Identify the most resource-intensive functions across your applications: - **CPU View:** Find functions consuming the most CPU time - **GPU View:** Discover GPU kernels and CPU-side GPU operations - **Sandwich View:** See callers and callees for any function to understand execution context - Filter by language, service, or infrastructure component **Perfect for:** Code-level optimization and understanding execution patterns. ### [GPU & CPU Flamegraphs](flamegraphs/) - Interactive Call Stack Visualization Explore your application's execution flow with interactive flamegraphs: - **Mixed-Stack Visualization:** See call stacks across multiple runtimes in a single view - **Interactive Controls:** Pan, zoom, search, and focus on specific functions - **CPU & GPU Profiles:** Analyze both CPU performance and GPU utilization patterns - **Advanced Filtering:** Group by executable, script, host, or thread for targeted analysis **Perfect for:** Understanding execution flow, identifying bottlenecks, and visualizing performance patterns across your entire stack. ### [Top Entities](top-entities.md) - Infrastructure Analysis Strategic macro-level view of resource consumption: - **Hosts:** See which machines are consuming the most resources - **Namespaces/Pods:** Understand Kubernetes resource distribution - **Containers:** Identify resource-heavy containers - **Main Executables:** Track consumption by process or runtime **Perfect for:** Infrastructure planning, resource allocation decisions, and finding system bottlenecks. ### [Diff Functions](diff.md) - Performance Comparison Compare performance between different states: - **Release Validation:** Before vs after deployment comparison - **A/B Testing:** Compare feature variants or configurations - **Blue/Green Deployments:** Validate performance during traffic migration - **Optimization Tracking:** Measure the impact of code changes **Perfect for:** Detecting regressions, validating optimizations, and measuring change impact. ## Essential Tools ### [Filtering Data](filtering-data.md) - Focus Your Analysis Master the filtering system to slice and dice your data: - **Click & Apply:** Quick filtering from dropdown menus - **Query Mode:** Advanced filtering using CEL syntax and OTEL attributes - **Global Filters:** Apply filters across all views consistently ### [Unit Reference](unit-reference.md) - Understanding Metrics Essential guide to Zymtrace measurements: - **CPU Modes:** Absolute vs relative percentage calculations - **Time Metrics:** Self time vs total time explained - **Core Units:** Millicores, cores, kilocores, and megacores - **GPU Metrics:** Understanding GPU profiling data ## Recommended Workflow Here's how to effectively use Zymtrace for performance analysis: ### 1. Start with Efficiency IQ - Get an overview of your system's resource consumption - Identify the top resource-consuming functions and entities - Toggle between absolute and relative CPU modes based on your analysis goal ### 2. Apply Filtering - Use entity filters to focus on specific services, environments, or time periods - Combine multiple filters for precise analysis (e.g., specific pods + Python functions) - Switch between click-and-apply and query mode based on complexity ### 3. Deep Dive Analysis - **For code optimization:** Use Flamegraphs, Top Functions → Sandwich View - **For infrastructure decisions:** Use Top Entities → drill Flamegraphs or Top functions - **For release validation:** Use Diff to compare before/after states :::info Need Help? If you have questions or need assistance, reach out to us at [support@zymtrace.com](mailto:support@zymtrace.com) or join our community discussions. ::: --- # Off-CPU Profiling URL: https://docs.zymtrace.com/features/off-cpu-profiling # Off-CPU Profiling CPU profiles show where threads spend time running. Off-CPU profiles show where they spend time waiting: on locks, disk or network I/O, sleeps, or a free CPU. Use them when a service is slow but its CPU usage is low. Off-CPU flamegraph filtered to a Python executable: worker threads blocked in lock_PyThread_acquire_lock down to futex_wait, with scheduler latency frames at the bottom of the stacks In this example, filtered to a Python application, most blocked time comes from worker threads waiting to acquire a lock (`lock_PyThread_acquire_lock` → `futex_wait`). ## Reading an off-CPU profile Open **CPU → Flamegraph** and select the **Off-CPU** tab. The chart shows blocked time over the selected range, and frame widths in the flamegraph show blocked time instead of CPU time: - **Blocked Time (total):** time threads spent off-CPU in a function and everything it called. - **Blocked Time (self):** time threads spent off-CPU in the function itself. The leaf frames show what threads were waiting on. For example, `futex` usually means a lock or condition variable, `epoll_wait` or `poll` means waiting for network or file I/O, and `nanosleep` means an explicit sleep. Follow the stack up to find the code that's waiting. ### Scheduler latency **Scheduler latency** frames at the bottom of a stack show the time between a thread being woken up and actually running, that is, time spent waiting for a CPU. A large share of scheduler latency points to CPU contention on the host rather than slow I/O or locks. To hide these frames, click the settings icon next to **Scheduler latency** in the flamegraph legend, clear **Show Scheduler latency Frames**, and click **Apply**. ## Enabling off-CPU profiling Off-CPU profiling is off by default. Enable it from the UI or with a profiler flag. ### From the UI 1. Go to **Settings → Agent Config** and open the **Off-CPU** tab. 2. Click **Add Rule**. 3. Enter a CEL expression for the hosts to profile, or leave it empty for all hosts. 4. Turn on **Off-CPU profiling**, optionally set an expiry, and click **Add rule**. The change applies without restarting the profiler. Off-CPU profiling is a host-wide setting, so rules can target hosts but not individual processes. Add rule: Off-CPU form with project, CEL expression, Off-CPU profiling toggle, and expiry ### With a profiler flag Pass `-enable-off-cpu` to the profiler, or set `ZYMTRACE_ENABLE_OFF_CPU=true`. With Helm, add it to `profiler.args`: ```bash helm upgrade --install profiler zymtrace/profiler \ --namespace zymtrace \ --set profiler.args[0]="--collection-agent=ZYMTRACE_URL" \ --set profiler.args[1]="--enable-off-cpu" ``` | CLI Argument | Environment Variable | Description | |--------------|---------------------|-------------| | `-enable-off-cpu` | `ZYMTRACE_ENABLE_OFF_CPU` | Enables off-CPU profiling. Default is false. | | `-off-cpu-sampling-rate` | `ZYMTRACE_OFF_CPU_SAMPLING_RATE` | Off-CPU sampling frequency in Hz. Setting a non-zero value also enables off-CPU profiling. Default is 19. | See [Profiler ENV & CLI Args](../install/profiler/profiler-configuration.md) for all profiler options. --- # Recommendations URL: https://docs.zymtrace.com/features/recommendations # Recommendations Zymtrace continuously finds the code paths that burn the most CPU and GPU time, groups them by the application that runs them, and uses the AI model you've configured to write one ranked, evidence-backed performance report per application. Each report names the problem, points to the exact frames, and lists fixes with estimated savings in CPU cores or GPU time. Application Recommendations list with CPU and CUDA sections, each recommendation showing a title, summary, tags, peak usage, hot trace count, and the application's scope ## Recommendations or the AI Assistant? | | Recommendations | AI Assistant | | --- | --- | --- | | How it runs | Automatically, in the background | On demand | | Scope | The heaviest code paths across your fleet | The application or time window you choose | | Best for | An up-to-date, ranked list of where CPU and GPU time goes | Investigating a specific service, incident, or time range | An application gets a recommendation only when its code accounts for a meaningful share of the project's CPU or GPU time. For anything else, use the [AI Assistant](../ai-assistant/using-ai-assistant.md). ## CPU and CUDA recommendations CPU and CUDA recommendations are generated separately and listed on their own tabs. - **CPU:** ranked by CPU time. Savings are estimated in CPU cores. - **CUDA:** ranked by the GPU time of the kernels each code path launched. The analysis covers both the launching code and the kernels, and savings are estimated in GPU time. :::note Recommendations for AMD ROCm are coming soon. ::: ## How it works Recommendations run continuously. There's nothing to trigger, and new results usually appear within minutes. ```mermaid flowchart LR A[Find the heaviest
code paths] --> B[Group them
by application] B --> C[Analyze with
your AI model] C --> D[Update as the
workload changes] D --> A ``` 1. **Find:** Zymtrace identifies the code paths (hot traces) that use the most CPU or GPU time. 2. **Group:** hot traces are grouped by application: a Kubernetes deployment, a container, or an executable. An application that runs in several clusters gets one recommendation. 3. **Analyze:** the AI model reviews the call trees, source locations, and most expensive functions, then writes a diagnosis and ranked actions with estimated savings. 4. **Update:** recommendations are regenerated automatically as the workload changes. ## Enabling recommendations Configure recommendations in **Settings → AI Config → Recommendations**. They use the AI provider set up in [Configure AI Assistant](../ai-assistant/configure-ai-assistant.md). AI Configuration settings, Recommendations tab, with the Automatic recommendations toggle, analysis scope, run cadence, and model settings | Setting | Description | | --- | --- | | **Automatic recommendations** | Turns recommendations on or off. | | **Window size** | How much recent profiling data to analyze. Default: 6 hours. | | **Minimum CPU usage to analyze a trace** | The share of the project's CPU time a code path must use to be analyzed. Default: 0.5%. | | **Step interval** | How often a new recommendation is produced. Default: every minute. | | **Provider** and **Model** | The AI model that writes recommendations. | | **Separate models per step** | Uses a different model for each analysis step. See [Reducing token cost](#reducing-token-cost). | | **Token limits** | Maximum input and output tokens per hour. Leave empty for no limit. | ### Reducing token cost {#reducing-token-cost} Each recommendation is produced in two steps: - **Trace examination:** the in-depth analysis that produces the diagnosis and actions. - **Summary and classification:** a short title, description, and tags. Turn on **Separate models per step** to keep a strong model for trace examination and use a smaller, lower-cost model for the summary. This reduces token cost while the strong model still does the analysis. Use **Token limits** to cap spend. ## Reading a recommendation Open **Recommendations** and choose the **CPU** or **CUDA** tab, or **All** for both. Recommendations are sorted by peak usage, so the heaviest applications come first. CUDA recommendation for a vLLM container, showing the issue detected, GPU time over the analysis window, and numbered recommended actions with estimated savings Each recommendation includes: - **Issue detected:** what's wrong, where it runs, and the time range. - **Usage over time:** CPU cores or GPU time for each hot trace. - **Recommended actions:** ranked fixes with estimated savings. - **Analysis:** the diagnosis and supporting evidence, with source locations. - **Affected entities:** the clusters, deployments, pods, and hosts involved. - **Technical details:** the model's confidence, lower-priority ideas, and the hot traces analyzed. CPU recommendations use the same layout, with usage and savings in CPU cores and an estimated annual cost: CPU recommendation for a .NET Kubernetes deployment, showing peak cores used, estimated annual cost, cores over time, and recommended actions with estimated savings in cores Each recommendation page has these actions: | Action | Description | | --- | --- | | **Dismiss** / **Restore** | Hides or restores a recommendation. | | **Regenerate** | Runs the analysis again. | | **Refresh traces** | Re-collects the heaviest hot traces, then runs the analysis again. | | **Suppress** | Excludes a hot trace you've accepted as expected cost. | ## Recommendations on flamegraphs Every CPU and CUDA flamegraph has its own recommendations. Click **Recommendations** above the flamegraph to see the auto-generated recommendations that match your current filters. To get a recommendation for what you're viewing right now, click **Generate recommendation**. CPU flamegraph with the Application Recommendations panel open, listing recommendations that match the current filters, with Generate recommendation and Refresh buttons Recommendations also appear on the [Efficiency IQ](./efficiency-iq.md) dashboard and through the [Zymtrace MCP server](../mcp/index.md). ## Slack notifications Send new recommendations to a Slack channel so your team sees them without opening Zymtrace. See [Slack](../integrations/slack.md). ## Limitations - **Review before you act.** Each action includes its evidence and a confidence level. Savings are estimates. - **On-CPU and CUDA only.** Off-CPU and allocation profiles aren't covered yet. --- # K8s Resource IQ URL: https://docs.zymtrace.com/features/right-sizing # K8s Resource IQ K8s Resource IQ compares the CPU and memory requests and limits of your Kubernetes containers with their observed usage. It shows how much requested capacity you can reclaim, which containers need more, and the values to apply. K8s Resource IQ page showing reclaimable CPU and memory, requested capacity bars, and a table of pods with current requests, usage, recommended values, and reclaimable capacity ## Capacity summary - **Reclaimable CPU and memory:** capacity requested but not needed, as a total and as a share of what's requested. Capacity needed by under-provisioned containers is shown separately. - **Requested capacity:** for CPU and memory, how much of the total request is used and how much can be reclaimed. - **Under-provisioned:** containers that use more than they request. - **No request set:** containers without a request, and how much the suggested requests would reserve. - **Cut withheld:** containers where a reduction looked possible but isn't suggested, for example because they're CPU throttled. They keep their current request. Group the table by **Clusters**, **Namespaces**, **Deployments**, **Pods**, or **Containers**. Each container row shows current usage, requests and limits, the **Recommended** request, and how much it frees up or needs. ## How sizing works Suggestions are based on usage over the sizing window, the **Last 14 days** by default: - **CPU** is sized from usage percentiles (P50, P90, and P99) across the window. - **Memory** is sized from the working set: memory usage minus inactive page cache. Kernel memory counts; cached files don't. - **Safety checks**, such as CPU pressure and daily memory peaks against the limit, can hold back a reduction. A container is sized once it has at least 7 days with enough data. ### Getting suggestions sooner Turn on **Size from fewer than 7 days** to size containers with less history. This is useful in the first 14 days after you deploy Zymtrace, or for new workloads, before a full sizing window is available. Suggestions based on fewer days reflect less of your traffic pattern, so review them before you apply them. ## Applying a suggestion Open a row's menu and choose **View details** to see: - **Recommendation:** current and suggested request and limit for CPU and memory, and a `resources` block to paste into your container spec. Suggestions can raise values as well as lower them. For example, a CPU-throttled container gets a higher CPU limit. - **CPU usage per pod** and **Daily memory peaks** over the sizing window. - **Live pods** seen in the last hour. - **Sizing inputs** and **Evidence** behind the suggestion. Details for a CPU-throttled pod: the suggestion raises the CPU request and limit and the memory request, with a resources YAML block, CPU usage per pod, daily memory peaks, and live pods To copy the values directly, choose **Copy resources YAML** from the row menu. To see what's using the CPU, choose **Analyze with CPU Flamegraph** or **View CPU Top Functions**. --- # Top Entities URL: https://docs.zymtrace.com/features/top-entities import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; # Top Entities The Top Entities view is your strategic starting point for continuous profiling. It provides a macro-level overview of performance data, grouped by infrastructure components across your entire system. This is your primary tool for understanding resource distribution and quickly identifying which part of your infrastructure is consuming the most compute. ## When to Use Top Entities? This view allows you to answer high-level questions about resource consumption before diving into specific code optimization. | Question | Value of Top Entities View | | :--- | :--- | | **"Which component is the bottleneck?"** | **Pinpoint Resource Hogs:** Instantly see which Host, Namespace, or Pod is consuming the highest percentage of CPU or GPU resources. | | **"How are resources allocated across my apps?"** | **Validate Infrastructure Decisions:** Quickly confirm if your resource budgeting and horizontal scaling are working as expected across your entire fleet. | | **"Where should I start optimizing?"** | **Prioritize Effort:** Drill down directly from the most resource-intensive entity to its **Top Functions** to find the exact code consuming the resources. | | **"Which process is the root cause?"** | **Process-Level Analysis:** Use the **Main Executables** view to track resource consumption by language runtime or process name, regardless of where they run. | ## What is an Entity? An **entity** in observability refers to any distinct object or component in your system that produces telemetry (profiles, metrics, logs, traces, or events) and can be observed, monitored, and analyzed. In **Zymtrace**, entities are organized to provide both infrastructure and application context: - **Hosts** – Physical or virtual machines running your workloads - **Namespaces** – Kubernetes namespaces organizing your resources - **Pods** – Kubernetes pods running your containers - **Deployments** – Kubernetes deployments managing your applications - **Containers** – Individual containers running your code - **Main Executables** – The primary process executable :::tip **Click any entity to drill down into its [Top Functions](/features/top-functions)** or CPU/GPU flamegraphs, allowing you to move from infrastructure-level analysis to function-level optimization. :::
![Top Entities View](../../static/img/features/top-entities.png) *Top Entities*
## Entity Types Top Entities can be viewed across different groupings to help you understand resource consumption across your infrastructure. From a Top Entity page, you can navigate directly to the details page for that entity type, which displays key monitoring metrics. These metrics are collected using eBPF with minimal performance overhead. Depending on the entity type, you can also access CPU and GPU flamegraphs, as well as top functions, for example, GPU machines will include GPU-specific insights. Let's look at the detailed monitoring page for some of the entities. :::info Note each metric point is interactable, you can either click to expand it or navigate to corresponding profiles for the given data point. ::: ### Hosts Most entity types provide similar views with consistent metrics across your infrastructure. Below is an example of the detailed monitoring page for **Hosts**. #### Host Detailed View
![Host Metrics](../../static/img/features/host-metrics.webp) *Host-level CPU and memory monitoring metrics*
![GPU Metrics](../../static/img/features/gpu-metrics.webp) *GPU utilization and memory monitoring metrics*
### Kubernetes Entities Zymtrace collects metrics and profiles for the following Kubernetes entities. Each entity type provides a consistent performance view with the same metrics, profiles, and drill-down capabilities. Here's a container list view:
![GPU Metrics](../../static/img/features/containers-list.png) *Containers entity list view*
#### Container Detailed View You can toggle between host and GPU metrics. Each metric data point is interactive. Click any point to drill down into the corresponding GPU or CPU profiles for that time range.
![K8s Container Metrics](../../static/img/features/container-metrics.png) *Container metrics*
![GPU Container Metrics](../../static/img/features/containers-gpu-metrics.png) *GPU Container Metrics*
### Main Executables **Main Executables** differ from other entity types by grouping data by the primary process executable rather than infrastructure components. This view is particularly useful for: - **Process-level analysis** – Understand which executables consume the most resources across your entire infrastructure - **Language runtime comparison** – Compare resource usage across different runtimes (Python, Java, Node.js, etc.) - **Cross-service patterns** – Identify if certain executables or runtimes consistently consume more resources regardless of where they run - **Optimize critical processes** – Target the most resource-intensive executables for optimization This view helps you answer questions like "How much CPU do all my Python processes consume?" or "Are our Java services more resource-intensive than our Go services?"
![GPU Metrics](../../static/img/features/main-exec.png) *GPU utilization and memory monitoring metrics*
#### Top Origins When viewing a main executable's details, you'll see **Top Origins** showing where this executable is running: - **Containers** – Which containers are running this executable and their resource consumption - **Hosts** – Which hosts are running this executable and their resource consumption This breakdown helps you identify if resource consumption patterns vary by deployment location or if specific instances need attention. #### Dynamic Shared Objects The **Dynamic Shared Objects** section lists all libraries (shared objects/DLLs) that were loaded by the main executable. For each library, you can see: - **Cores Used (self)** – CPU cores consumed by code executing directly in this library - **Cores Used (total)** – Total CPU cores including functions this library calls - **Self CPU %** – Percentage of CPU time spent executing code in this library - **Total CPU %** – Percentage of CPU time including all functions called by this library - **Self Time** – Time spent in the library's own code - **Total Time** – Total time including all called functions This view is essential for identifying which libraries contribute most to your executable's resource consumption, helping you target optimization efforts at the library level. ## Actions From any entity, you can: - **View Entity Details** – See detailed information about the entity - **Analyze with Flamegraph** – View a flamegraph for all functions in this entity - **View CPU Top Functions** – See the top CPU-consuming functions in this entity - **View GPU Top Functions** – See the top GPU-consuming functions in this entity - **Add as Filter** – Filter the entire view to show only data from this entity --- # Top Functions URL: https://docs.zymtrace.com/features/top-functions import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; # Top Functions The Top Functions view presents an ordered list of functions that Zymtrace samples most often. From this view, you can spot the most expensive functions across your entire fleet, applying [filters](./filtering-data) to drill down into individual components. **Click any function to open the [Sandwich view](#sandwich-view)**, which shows the function's callers (what called it) and callees (what it called), giving you the complete execution context to understand and optimize your code. :::tip We strongly recommend you add frame type filters in combination with the entity of your choice to narrow down your search. For example, combine `k8s.pod.name == "my-app"` with `profile.frame.type == "python"` to focus on Python functions in a specific pod. ::: ## GPU Top Functions GOU functions are ranked by how much GPU time they have taken, helping you identify: - **CPU-side optimization opportunities** - GPU time enqueued by CPU code to determine where to optimize on the CPU side - **Slow CUDA kernel executions** - Identify kernels that are executing inefficiently on the GPU - **Kernel fusion opportunities** - Find where multiple kernel launches can be combined for better performance ![GPU Top Functions View](../../static/img/features/top-functions-gpu.png) *GPU Top Functions showing GPU consumption across services* ### Understanding the Data The table below shows the profiling data displayed for each function: | Field | Description | |--------|-------------| | **Self Time** | For CUDA frames, Self Time reflects the time spent executing this particular function on the GPU, excluding any functions it called. For CPython and Native frames this is always zero because they are not actually executed on the GPU. | | **Total Time** | For CPython and native frames, Total Time indicates the GPU time consumed by CUDA kernels launched by the function (including its callees); for CUDA frames, it reflects time spent executing the function's body and any functions it called on the GPU. | | **Self GPU %** | For CUDA frames, Self GPU reflects the percentage of time spent executing this particular function on the GPU, excluding any functions it called. For CPython and Native frames this is always zero because they are not actually executed on the GPU. | | **Total GPU %** | For CPython and native frames, Total GPU indicates the percentage of GPU time consumed by CUDA kernels launched by the function (including its callees); for CUDA frames, it reflects percentage of time spent executing the function's body and any functions it called on the GPU. | :::info Frame Types The data above varies based on the frame type: - **CUDA frames**: Represent actual GPU kernel execution - **CPython/Native frames**: Represent CPU code that launches GPU kernels ::: Learn more about this data in the [Unit Reference](/features/unit-reference). ## CPU Top Functions Functions are ranked by how frequently they appear in CPU profiling samples, helping you identify: - **Performance hotspots** - Functions consuming the most CPU time - **Optimization targets** - Where improvements will have the biggest impact - **Resource distribution** - How CPU time is spread across your codebase ![CPU Top Functions View](../../static/img/features/top-functions-cpu.png) *CPU Top Functions showing CPU consumption across services* ### Understanding the Data The table below shows the profiling data displayed for each function: | Field | Description | |--------|-------------| | **Cores Used (self)** | Indicates the amount of CPU cores used per second (on average) in the function's body, excluding the cores used in any functions it called. | | **Cores Used (total)** | Indicates the amount of CPU cores used per second (on average) in the function's body, including the cores used in any functions it called. | | **Self CPU %** | Indicates the percentage of CPU time spent in the function's body, excluding the time spent in any functions it called. | | **Total CPU %** | Indicates the percentage of CPU time spent in the function's body, including the time spent in any functions it called. | Learn more about this data in the [Unit Reference](/features/unit-reference). Filters help you drill down from infrastructure-wide view to specific components. ## Sandwich View The typical flamegraph will break down resource utilization by function, but it can be difficult to see how much time is spent in the function itself vs how much time is spent in the functions it calls. Sandwich view solves this problem by splitting a flamegraph into two sections: When you click on any function, Zymtrace displays a **sandwich view** showing the complete call context: - **Callers** - The functions that called the function in question (its "parents") - **Selected Function** (middle, highlighted) - The function you clicked - **Callees** - The functions that the function in question called (its "children") This view helps you understand the complete execution path and identify optimization opportunities by seeing what calls your expensive function and what it calls in return. --- # Unit Reference URL: https://docs.zymtrace.com/features/unit-reference import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; # Unit Reference This page explains the key metrics and units used throughout Zymtrace. ## CPU % Mode: Absolute vs Relative The **CPU % Mode** toggle allows you to switch between two different ways of calculating CPU percentages: - **Abs (Absolute)** – Shows real CPU usage percentages—just like what you'd see in tools such as `htop` or `btop`. For example, if your cluster has 2 machines with 8 cores each (16 cores total), and your workloads are consuming 4 cores on each machine (8 cores total), Zymtrace will display 50% CPU usage for that service. - **Rel (Relative)** – Presents CPU utilization in relation to other workloads you're profiling, helping you see which services dominate your compute resources. This shows each entity's percentage of the total consumed compute across all profiled workloads. Use **Absolute mode** to understand real infrastructure utilization. Use **Relative mode** to understand the distribution of CPU consumption among actively profiled workloads. ### Core Size Reference Core usage values are displayed with the following units: - **mc** - millicore (1/1000 core) - **c** - core - **kc** - kilocore (1000 cores) - **Mc** - megacore (1,000,000 cores) ## Self vs Total Time Understanding the difference between self and total time is crucial for identifying optimization opportunities: ### Self Time Time spent executing the function's own code only, excluding any functions it calls. - Indicates if the function itself is expensive - High self time = optimize the function's algorithm - Measures the direct cost of the function's instructions ### Total Time Time including all called functions (the function plus everything it calls). - Shows the full cost of calling this function - High total time with low self time = optimize what it calls - Measures the complete execution path ### Example ``` Function: processData() Self CPU: 2% (function is fast) Total CPU: 45% (but calls expensive operations) → Optimize what it calls, not the function itself ``` In this example, `processData()` spends very little time in its own code (2%), but the functions it calls consume 43% of CPU time. The optimization opportunity is in the called functions, not `processData()` itself. ## Unit Definitions Zymtrace uses specific units and metrics to measure and display resource consumption across your infrastructure. Understanding these units is essential to fully utilize the platform. The following sections detail the units used for GPU and CPU profiling. ## GPU Units | Unit | Description | |------|-------------| | **Self Time** | For CUDA frames, Self Time reflects the time spent executing this particular function on the GPU, excluding any functions it called. For CPython and Native frames this is always zero because they are not actually executed on the GPU. | | **Total Time** | For CPython and native frames, Total Time indicates the GPU time consumed by CUDA kernels launched by the function (including its callees); for CUDA frames, it reflects time spent executing the function's body and any functions it called on the GPU. | | **Self GPU %** | For CUDA frames, Self GPU reflects the percentage of time spent executing this particular function on the GPU, excluding any functions it called. For CPython and Native frames this is always zero because they are not actually executed on the GPU. | | **Total GPU %** | For CPython and native frames, Total GPU indicates the percentage of GPU time consumed by CUDA kernels launched by the function (including its callees); for CUDA frames, it reflects percentage of time spent executing the function's body and any functions it called on the GPU. | :::info Frame Types The data above varies based on the frame type: - **CUDA frames**: Represent actual GPU kernel execution - **CPython/Native frames**: Represent CPU code that launches GPU kernels ::: ### When to Use Self vs Total (GPU) **Use Self GPU Time** to identify expensive GPU kernels, find kernels that need optimization, or look for inefficient CUDA implementations. **Use Total GPU Time** to understand the full GPU cost of a function, find which high-level functions trigger expensive GPU operations, or decide what GPU workloads to optimize or replace. ## CPU Units | Unit | Description | |------|-------------| | **Cores Used (self)** | Indicates the amount of CPU cores used per second (on average) in the function's body, excluding the cores used in any functions it called. | | **Cores Used (total)** | Indicates the amount of CPU cores used per second (on average) in the function's body, including the cores used in any functions it called. | | **Self CPU %** | Indicates the percentage of CPU time spent in the function's body, excluding the time spent in any functions it called. | | **Total CPU %** | Indicates the percentage of CPU time spent in the function's body, including the time spent in any functions it called. | ### When to Use Self vs Total (CPU) **Use Self CPU Time** to identify hot loops or expensive algorithms, find functions that need code optimization, or look for inefficient implementations. **Use Total CPU Time** to understand the full cost of a code path, find which high-level functions are most expensive, or decide what functionality to optimize or replace. --- # Quick Start URL: https://docs.zymtrace.com/getting-started/ import Link from '@docusaurus/Link'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import CodeBlockWithVersion from '@site/src/components/CodeBlockWithVersion'; # Quick Start :::info What you'll get Deploy in minutes. No code changes. Zymtrace immediately shows you what is hurting performance across GPU and CPU workloads, down to the exact function, kernel, and stall reason — with AI-powered fixes and [MCP](/mcp) access from your IDE. ![vLLM GPT-OSS inference flamegraph with AI assistant recommendations](/img/vllm-gpt-oss.png) This is a `GPT-OSS` inference run on vLLM. The flamegraph on the left shows the full call path from Python down to CUDA kernels. The AI assistant on the right identified that `hal.matmul` is consuming ~50% of GPU time due to suboptimal kernel tiling, and that `_prepare_inputs` is stalling the GPU with synchronous host-to-device copies on every forward pass. Exact fixes for both are surfaced automatically. ::: :::tip Get More Value – Let's Talk! There's a good chance you could unlock more value from Zymtrace, and do it faster by having a quick chat with us about your use cases. We're a team of technical founders, not your typical pushy salespeople, and there's probably a thing or two we can learn from each other. [Reach out to us here](https://www.zymtrace.com/getstarted) and we’ll set you up with an advanced trial licence. You're also welcome to continue with the self-guided trial below. ::: ## Get Started in Just 3 Simple Steps! Zymtrace is shipped with a free perpetual trial license — use it at no cost. ### Step 1: Add the Zymtrace Helm repository [![Artifact Hub](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/zymtrace)](https://artifacthub.io/packages/search?repo=zymtrace) :::info Helm Chart Source The Helm chart source code is available on GitHub: [zystem-io/zymtrace-charts](https://github.com/zystem-io/zymtrace-charts/tree/main/charts/) ::: To get started, add the Zymtrace Helm repository by running the following command: ```sh helm repo add zymtrace https://helm.zystem.io ``` To verify and list available charts and versions, use: ```sh helm search repo zymtrace --versions ``` ### Step 2: Install the backend components Deploy the backend components using Helm: ```sh helm upgrade --install backend zymtrace/backend \ --namespace zymtrace \ --create-namespace \ --reset-then-reuse-values \ --debug \ --atomic ``` ### Step 3: Install the profiler as a DaemonSet :::tip The `collection-agent` arg should point to the gateway service or ingress, if you exposed it. ::: Deploy the profiler as a DaemonSet using the following command: **GPU Profiling enabled:** ```bash helm install profiler zymtrace/profiler \ --namespace zymtrace \ --create-namespace \ --set profiler.cudaProfiler.enabled=true \ --set profiler.args[0]="--collection-agent=zymtrace-gateway.zymtrace.svc.cluster.local:80" \ --set profiler.args[1]="--disable-tls" ``` > **Note:** For NVIDIA GPU metrics, the profiler needs to locate the NVML library: add `--nvml-auto-scan` to the profiler args to detect it automatically, or `--nvml-path=/path/to/libnvidia-ml.so` if you know the exact path. For a step-by-step guide to GPU profiling on NVIDIA and AMD with Binary, Docker, or Kubernetes, see the [GPU Profiler Quick Start](../install/profiler/gpu-profiler-quick-start.md). For advanced configuration, see [Profiling CUDA Applications](../install/profiler/profiling-gpu-workloads.md) and [AMD GPU Metrics & Profiles](/gpu/amd/gpu-metrics). **For CPU Profiling only:** ```sh helm install profiler zymtrace/profiler \ --namespace zymtrace \ --create-namespace \ --debug \ --set profiler.args[0]="--collection-agent=zymtrace-gateway.zymtrace.svc.cluster.local:80" \ --set profiler.args[1]="--disable-tls" ``` We also have [`arm64` and `x86/amd64`](../install/profiler/install-profiler#install-binary) binary distributions of the Zymtrace profiler. ### Step 1: Create directory and download Docker Compose file Create a project directory and download the Zymtrace Docker Compose configuration: { `mkdir Zymtrace && cd Zymtrace curl -LO https://dl.zystem.io/zymtrace/{current-version}/noarch/docker-compose.yml` } ### Step 2: Start the backend services Launch all backend services with Docker Compose: ```sh docker compose up -d --remove-orphans ``` This will start: - ClickHouse database - PostgreSQL database - MinIO object storage - Zymtrace backend services (gateway, ingest, symdb, identity, ui, worker) :::info License Key (Optional) If you're using a licensed version, create a `.env` file with your license key. This is not required for the free trial version. ```bash echo 'ZYMTRACE_LICENSE_KEY="paste-key-here"' > .env ``` Replace `paste-key-here` with your actual license key. ::: ### Step 3: Access the UI Navigate to http://localhost:8080 to access the Zymtrace interface. For profiler installation with Docker, refer to the [profiler installation guide](../install/profiler/install-profiler.md#install-docker). ## Start Using Zymtrace: Efficiency IQ Once deployed, access the **Efficiency IQ** page for a high-level overview of your fleet's performance. From this view, you can quickly spot performance spikes and identify emerging usage patterns, allowing you to optimize your system effectively. You can also navigate to flamegraphs and functions page from the Efficiency IQ dashboard. You're all set! ---

📐 Architecture

Learn about Zymtrace's architecture and components

Learn More

✨ Install Zymtrace Backend

Deploy Zymtrace backend

Learn More

⚡ Install Zymtrace Profiler

Install Zymtrace agent with Helm, Kubernetes manifests, Docker...

Learn More
--- # GPU Profiler Architecture URL: https://docs.zymtrace.com/gpu-profiler-architecture # Zymtrace GPU Profiler Architecture ## Overview Zymtrace GPU profiling is heterogeneous by design, built for multi-silicon fleets: it supports **NVIDIA GPUs**, **AMD GPUs**, and **AWS Neuron** accelerators, all reporting into the same backend and the same views. Support for **Google TPUs** and **Cerebras** is coming soon. On NVIDIA hardware, Zymtrace provides visibility down to `CUDA kernels`, disassembles `CUDA SASS` mnemonics, exposes GPU stall reasons, and correlates it all back to the `CPU traces` that launched the kernels, without requiring recompilation. :::note Multi-silicon architecture This page uses the NVIDIA (CUDA) profiler architecture as the base. The same approach mostly applies to AMD ROCm: a vendor tool library (`libzymtracerocmprofiler.so`) is loaded into the ROCm workload, kernel activity is correlated with the CPU stacks that launched it, and the combined traces flow through the same backend and flamegraph. See [AMD GPU Metrics & Profiles](/gpu/amd/gpu-metrics) for the AMD specifics, and the [Metrics Reference](/gpu/metrics-reference#neuron) for the AWS Neuron metrics. ::: :::tip Correlating CPU stack traces to CUDA kernels is a key differentiator in our approach, because most GPU idle cycles happen when the GPU is waiting on the CPU to finish execution. ::: :::info GPU Metrics GPU profiling provides detailed, CUDA kernel-level analysis for deep optimization insights. For high-level monitoring, consider starting with [GPU Metrics](/gpu/nvidia/gpu-metrics). Metrics help identify performance trends and anomalies, while profiling validates hypotheses with detailed CUDA kernel execution analysis. Together, they enable a complete GPU performance analysis workflow. ::: ## How the CUDA profiler works The CUDA profiler is not a standalone component. It's a module within the [Zymtrace profiler](/architecture#zymtrace-profiler), which can be enabled by simply setting a flag. It consists of `libzymtracecudaprofiler.so`, a library that is shipped with the Zymtrace profiler. If enabled, the library is extracted into a specified directory upon startup and can be loaded into your CUDA workload via CUDA's `CUDA_INJECTION64_PATH` environment variable. Refer to the Kubernetes and Docker examples [here](./install/profiler/profiling-gpu-workloads.md). The Zymtrace CUDA profiler collects information about kernel launches and completions. The profiler also samples high-granularity information about precisely which `GPU instructions (SASS)` are running on the GPU's compute cores and the reasons for what is currently preventing the kernel from making progress (stall reasons). **_These stall reasons provide clear indications on why the kernel is slow._** For example, there are stall reasons indicating that the GPU is waiting for a slow read from global memory or when it is waiting for an oversubscribed math pipeline. This information is pre-aggregated within the CUDA profiler and then sent out to `zymtrace-profiler`. CUDA profiler CUDA profiler Within `libzymtracecudaprofiler.so`, we place callbacks on all `CUDA` functions that can launch a `CUDA` kernel. `zymtrace profiler` detects the CUDA profiler and places a `uprobe` on an exported function that is called each time that a kernel is launched. The `uprobe`, in turn, invokes the CPU stack unwinder that we're also using for CPU profiling in general. The `BPF` code additionally collects the necessary information about the kernel launch that is sent along with the stack trace to enable correlation with the data that is collected in the `CUDA` profiler library. Once both the stack traces collected in `BPF` and the kernel launch/completion data arrive in `zymtrace profiler`, they are merged together and sent out as a single combined stack trace that spans from the user-mode code that enqueued the kernel into the CUDA kernel that got invoked on the GPU and, if enabled, the individual instructions and stall reasons. These traces are then sent out to our backend, ready to be visualized as a flamegraph within our UI. ## Zymtrace AI flamegraph anatomy The Zymtrace AI flamegraph displays stack traces from the CPU code down to the CUDA instruction offset. Our flamegraph renderer was built from the ground up in Rust and WebAssembly (WASM). :::info Refer to our blog post "[Zymtrace AI Flamegraph: Why We Ditched TypeScript for Rust and WebAssembly](https://zymtrace.com/article/zymtrace-ai-flamegraph/)" to learn more ::: The diagram below shows the general execution flow of the Zymtrace AI flamegraph.
Zymtrace AI execution flow
### use-case: GPT-OSS inference on vLLM The AI flamegraph below captures a complete inference pass running `GPT-OSS` via `vLLM`, providing end-to-end visibility with zero friction. GPU profiler - vLLM GPT-OSS inference Total GPU time for this pass is **98.5s**. Three problems are immediately visible. **Problem 1: CPU is stalling the GPU in `_prepare_inputs`** `_prepare_inputs` spends ~10.6s (10.7% of total) doing synchronous host-to-device copies for slot mapping and block table data. The flamegraph traces this directly to `BlockTable.convert_slot_mapping` and `lowest_bias_table`, which issue `cudaMemcpyAsync` calls that block the pipeline before each forward pass. The GPU sits idle waiting for data that the CPU could have staged earlier. The fix: prefetch the block table and slot mapping to GPU memory before the forward pass begins, or keep them resident on device across steps rather than re-copying each iteration. Ensuring the source tensors use pinned memory (verifiable via `tensor.is_pinned()`) is a prerequisite for async copies to actually overlap with computation. **Problem 2: Memory-bound GEMM in `compute_logits`** The bf16 GEMM kernels in `compute_logits` account for ~5.4s, with 20% of that time stalled on `smsp__pcsamp_warps_issue_stalled_long_scoreboard` — warps waiting on L1TEX data dependencies to resolve. The kernel is issuing dependent memory loads faster than L1 can service them, causing the warp scheduler to stall. This points to a memory access pattern that is not cache-friendly for the current tile configuration. **Problem 3: Suboptimal matmul tiling consuming 50% of GPU time** `hal.matmul` is the single largest cost at ~50% of total GPU time. The flamegraph shows multiple competing tiling variants (`a16/a16`, `semgu`) being dispatched, which is a signal that vLLM's kernel selection is not converging on an optimal configuration for this batch size and model shape. Running vLLM's profiling-based kernel selection (`--enforce-eager` off, with a warmup sweep) to pre-select the best tiling for the deployment configuration would recover significant time here. ### GPU Time The flamegraph tooltip contains GPU Time, broken down into total time and self time. #### GPU Time (Total) For `CPython` and `native` frames, Total GPU time indicates the GPU time (also shown as a relative percentage) consumed by `CUDA` kernels launched by the function (including its callees). For CUDA frames, it reflects the time spent executing the function's body. #### GPU Time (Self) For `CPython` and `native` frames, Self GPU time is always zero, as these frames are not executed on the GPU. For CUDA frames, Self GPU time reflects the time spent executing only the function's own body on the GPU. If PC sampling is enabled, timing is broken down into stall reasons, instructions, and PC offsets, with Self GPU time attributed only to offsets, enabling a fine-grained view of what happened during the kernel execution. --- # AMD GPU Metrics & Profiles URL: https://docs.zymtrace.com/gpu/amd/gpu-metrics # AMD GPU Metrics & Profiles AMD GPU metrics provide host-wide visibility into accelerator utilization, memory activity, power, temperature, and clocks. Use these metrics to identify an interesting host, device, or time window, then open the corresponding ROCm flamegraph to see the HIP kernels and CPU call stacks behind it. - **Metrics** reveal trends and resource bottlenecks. - **Profiling** attributes device time to HIP kernels, memory operations, synchronization, ROCTX ranges, and the CPU stacks that launched them. ## Requirements - An x86_64 host using the `amdgpu` kernel driver - A ROCm workload (ROCm 7.x or above) - The zymtrace profiler running on the host with GPU support enabled by your license - AMD SMI available to the profiler for metrics collection - `libdrm_amdgpu.so.1` available for GPU names, memory metrics, and driver information The profiler searches standard library paths automatically. Use `-amdsmi-path=/path/to/libamd_smi.so` or `-libdrm-path=/path/to/libdrm_amdgpu.so.1` only when the libraries are installed elsewhere. ## Enabling AMD GPU profiling The profiler ships the ROCprofiler SDK and the zymtrace ROCm tool library. Start each ROCm workload with these environment variables so ROCprofiler loads the tool when the process starts: ```bash ROCP_TOOL_LIBRARIES=/opt/zymtrace/profiler/libzymtracerocmprofiler.so ROCPROFILER_REGISTER_LIBRARY=/opt/zymtrace/profiler/librocprofiler-sdk.so.1 ``` For a containerized workload, mount the profiler's shared host directory and set both variables: ```bash docker run \ -v /var/lib/zymtrace/profiler:/opt/zymtrace/profiler:ro \ -e ROCP_TOOL_LIBRARIES=/opt/zymtrace/profiler/libzymtracerocmprofiler.so \ -e ROCPROFILER_REGISTER_LIBRARY=/opt/zymtrace/profiler/librocprofiler-sdk.so.1 \ your-gpu-image ``` AMD profiling cannot attach to an already-running GPU process: the environment variables must be present when the ROCm process starts. The **Add Data** flow in the zymtrace UI generates deployment-specific Docker, Helm, and binary commands. See the [GPU Profiler Environment Variables](/install/profiler/profiler-gpu-variables?gpu-vendor=amd) AMD tab for the complete reference of `ZYMTRACE_ROCMPROFILER__*` tuning variables. ## Available AMD GPU metrics | Metric | Description | Unit | |---|---|---| | `hw.amd_gpu.utilization` | GPU engine utilization | % | | `hw.amd_gpu.memory.activity` | Memory-controller activity | % | | `hw.amd_gpu.multimedia.utilization` | Multimedia engine utilization | % | | `hw.amd_gpu.memory.utilization` | VRAM currently in use | bytes | | `hw.amd_gpu.power` | Current power usage | mW | | `hw.amd_gpu.power_limit` | Configured power limit | mW | | `hw.amd_gpu.temp` | GPU edge temperature, when the ASIC exposes an edge sensor | °C | | `hw.amd_gpu.clock.graphics` | Current graphics clock | MHz | | `hw.amd_gpu.clock.mem` | Current memory clock | MHz | Metric availability depends on the GPU and AMD SMI support for that ASIC. Without `libdrm`, activity, power, temperature, and clock metrics can still be available, but GPU names, memory metrics, and driver information are unavailable. See the [Metrics Reference](../metrics-reference.md#amd-gpu) for each metric's type, aggregation, and the chart it drives. ## Drilling down from metrics into ROCm profiles Metrics, profiles, and CPU code are one connected workflow, not separate views. A typical investigation moves through three levels: 1. **Metrics.** Navigate to **Top Entities**, select an AMD GPU host, and inspect its metrics dashboard. A utilization, memory, power, temperature, or clock anomaly identifies the interesting device and time window. Clicking that data point scopes the analysis to it. 2. **ROCm profiles.** From the data point, open the ROCm flamegraph for that exact window. GPU time is broken down by HIP kernel, joined to the complete CPU call path that launched each one, from the application's Python frames through the framework and math libraries down to the kernel dispatch. 3. **The kernel and its launching CPU code.** Selecting a kernel frame opens its function details: the fully symbolized kernel name, its GPU time and share, its usage trend, the hosts it ran on, and the full CPU stack trace that launched it. ROCm flamegraphs can contain: - `rocm` frames for HIP kernel dispatches - `rocm-memory` frames for memory copies and fills - `rocm-sync` frames for synchronization operations - `rocm-roctx` frames for ROCTX ranges Profiles retain the GPU, stream, host process, and thread identity and are joined to the CPU stack that submitted the operation. ### Example: vLLM GPT-OSS on an MI325X **Level 1: metrics.** The dashboard for an MI325X host serving vLLM GPT-OSS shows utilization, memory, power, temperature, and clock behavior. Any of these charts is a click away from the profile view for the same device and time window. AMD GPU metrics dashboard for an MI325X host showing GPU utilization, memory activity, multimedia engine utilization, memory utilization, power with power limit, temperature, and graphics and memory clock frequencies **Level 2: the ROCm flamegraph.** GPU time is attributed through the full launch path: the vLLM engine's Python frames (`EngineCore.step`, `GPUModelRunner.execute_model`), the PyTorch dispatcher, the rocBLAS/hipBLASLt stack (`hipblasLtMatmul`, `rocblaslt_matmul`, `TensileLite::hip::SolutionAdapter::launchKernels`), and finally the HIP kernels executing on the device. Every kernel sits directly under the CPU code that launched it. ROCm flamegraph of a vLLM GPT-OSS container on an MI325X, showing Python, PyTorch, and rocBLAS frames down to the HIP GEMM kernel **Level 3: the kernel.** Selecting a kernel frame opens its function details. Here the kernel is the rocBLAS GEMM, shown with its fully symbolized name: ``` rocBLAS GEMM TN [bf16xbf16→bf16, accum=f32] MT=256x64x64 WG=32x8 w64 ``` That one name tells you the transpose configuration (`TN`), the data types (BF16 inputs and output with FP32 accumulation), the macro-tile size (`MT=256x64x64`), and the workgroup shape (`WG=32x8`, wave64). The details panel adds the kernel's GPU time and share, its AMD GPU usage trend over the selected window, the hosts it ran on, and the complete CPU stack trace that launched it, from `EngineCore.step` at the top of the vLLM engine down to the dispatch: the exact application code responsible for the device time. Function details for the rocBLAS GEMM TN bf16 kernel showing GPU time, AMD GPU usage trend, top origin host, and the launching stack trace ## Roadmap zymtrace is building AMD GPU support toward full parity with its NVIDIA capabilities. The following are on the roadmap for AMD GPUs: - Per-process GPU utilization and memory metrics - PCIe, NVLink or xGMI throughput metrics - ECC, SM efficiency, occupancy, and Tensor Core metrics - MIG-aware profiling - Instruction-level PC sampling, ISA attribution, and stall reasons - High-resolution GPU timeline - AI recommendations for GPU profiles --- # Inference Server Metrics URL: https://docs.zymtrace.com/gpu/inference-server-metrics # Inference Server Metrics Zymtrace automatically detects and extracts application-level metrics from popular AI inference servers. These metrics complement [GPU metrics](/gpu/nvidia/gpu-metrics) and [CPU profiles](/gpu-profiler-architecture) by surfacing serving-layer performance data (throughput, queue depth, latency, cache efficiency, and more) directly alongside your profiling data. Collection is host-side and works the same on NVIDIA and AMD GPU nodes. :::info Supported Inference Servers - **[vLLM](https://github.com/vllm-project/vllm)**: [metrics collected](./metrics-reference#vllm) - **[SGLang](https://github.com/sgl-project/sglang)**: [metrics collected](./metrics-reference#sglang) - **[NVIDIA Dynamo-Triton](https://developer.nvidia.com/triton-inference-server)** (NVIDIA hosts): [metrics collected](./metrics-reference#triton) ::: ## How It Works The profiler scrapes Prometheus-compatible metrics endpoints exposed by the inference server running on the same host. No changes to the inference server are required. The profiler automatically discovers the endpoint and begins collecting metrics at the interval configured by `-collect-metrics-interval` (default: 30s). Collected metrics are forwarded to the Zymtrace backend alongside profiles and GPU metrics, making it possible to correlate high token latency or low cache hit rates directly with CPU and GPU profiling data. ![Inference server metrics dashboard](/img/vllm-metrics.png) ## Enabling Inference Server Metrics Each inference server has a dedicated flag. All three are **enabled by default**, so no extra configuration is needed when the profiler is running alongside a supported server. | Inference Server | CLI Flag | Environment Variable | Default | |-----------------|----------|---------------------|---------| | vLLM | `-enable-vllm-metrics` | `ZYMTRACE_ENABLE_VLLM_METRICS` | `true` | | SGLang | `-enable-sglang-metrics` | `ZYMTRACE_ENABLE_SGLANG_METRICS` | `true` | | NVIDIA Dynamo-Triton | `-enable-triton-metrics` | `ZYMTRACE_ENABLE_TRITON_METRICS` | `true` | To explicitly disable metrics collection for a specific server, set the flag to `false`: ```bash # Disable only vLLM metrics sudo ./zymtrace-profiler \ -collection-agent=gateway.company.com:443 \ -enable-vllm-metrics=false ``` Or via environment variable: ```bash export ZYMTRACE_ENABLE_VLLM_METRICS=false sudo ./zymtrace-profiler -collection-agent=gateway.company.com:443 ``` ## Configuration Reference See the [Profiler ENV & CLI Args](/install/profiler/profiler-configuration) page for the full list of configuration options, including `-collect-metrics-interval` which controls how frequently all metrics (GPU, host, and inference server) are scraped. ## Viewing metrics To view the inference metrics in Zymtrace, expand the **Fleet-wide Metrics** entry in the sidebar, then select **vLLM**, **SGLang**, or **Dynamo-Triton**. ## Digging deeper with profiles Metrics tell you *what* is slow, profiles tell you *why*. When a metric data point looks anomalous, you can jump straight to the GPU flamegraph for that moment in time. Click any data point on a metric chart and select **Analyze with GPU flamegraph** from the pop-up menu. Zymtrace will open the flamegraph scoped to that exact timestamp, so you can immediately see which CUDA or HIP kernels were consuming resources when the metric spiked. The flamegraph below is scoped to a vLLM GPT-OSS container. GPU time is attributed from the vLLM engine loop (`EngineCore.step`, `GPUModelRunner.execute_model`) through CUDA graph replays down to the MoE matmul kernels (`_matmul_ogs_NNT_bf16xbf16xmxfp4_16x256x128x2`) and their SASS instructions and stall reasons. The AI assistant panel analyzes the same data and ranks the hotspots, here flagging host-to-device copy overhead in `_prepare_inputs` as the top optimization opportunity. CUDA flamegraph of a vLLM GPT-OSS container showing the engine loop, CUDA graph replays, MoE matmul kernels with SASS and stall reasons, and AI assistant hotspot analysis --- # Metrics Reference URL: https://docs.zymtrace.com/gpu/metrics-reference # Metrics Reference Zymtrace collects metrics across five layers: the **host**, **NVIDIA GPUs** (including MIG partitions), **AMD GPUs**, **AWS Neuron** accelerators, and **inference servers** (vLLM, NVIDIA Dynamo-Triton, and SGLang). This page lists every metric by category: its key, type, the aggregation applied, the dimensions it can be grouped by, and the chart it drives. To enable collection, see [NVIDIA GPU Metrics & Profiles](./nvidia/gpu-metrics.md), [AMD GPU Metrics & Profiles](./amd/gpu-metrics.md), and [Inference Server Metrics](./inference-server-metrics.md). The scrape frequency is controlled by `-collect-metrics-interval`; see [Profiler ENV & CLI Args](/install/profiler/profiler-configuration). ## Conventions - **Metric key** — the metric identifier queried by the dashboard. - **Temporal** — aggregation over time: `avg`, `max`, `rate`, `increase`. - **Spatial** — aggregation across series/hosts: `avg`, `sum`, `max`. - **Group-by** — the dimensions a chart breaks down by. *by process / script / container* means the metric is charted per main executable, per script (Python), and per container. - Latency histograms report the `p50`, `p95`, and `p99` percentiles. ## Dashboards Open the dashboards from **Fleet-wide Metrics** in the sidebar. | Dashboard | Route | |---|---| | Host | `/dashboards/host` | | NVIDIA GPU (incl. MIG) | `/dashboards/cuda` | | AMD GPU | `/dashboards/rocm` | | vLLM | `/dashboards/vllm` | | SGLang | `/dashboards/sglang` | | Dynamo-Triton | `/dashboards/triton` | | AWS Neuron | `/dashboards/neuron` | --- ## Host The **Process / Cgroup** toggle on the memory charts switches between resident-set memory (RSS) per process and working-set memory (WSS) per cgroup. | Metric key | Type | Temporal | Spatial | Group-by | Chart | Description | |---|---|---|---|---|---|---| | `process.utime` | gauge | avg | sum | — | CPU Utilization | User-mode CPU. | | `process.stime` | gauge | avg | sum | — | CPU Utilization | System/kernel-mode CPU. | | `process.utime` | gauge | avg | sum | process / script / container | CPU Consumer (User) | User-mode CPU by consumer. | | `process.stime` | gauge | avg | sum | process / script / container | CPU Consumer (System) | System-mode CPU by consumer. | | `process.wss` | gauge | avg | sum | pod / container / deployment / namespace | Memory Consumer (Cgroup) | Working-set memory by cgroup entity. | | `process.rss` | gauge | avg | sum | process / script / container | Memory Consumer (Process) | Resident-set memory by consumer. | | `process.wss` | gauge | max | sum | — | Memory Utilization (Cgroup) | Total WSS across containers. | | `process.rss` | gauge | max | sum | — | Memory Utilization (Process) | Total RSS across processes. | | `io.read_throughput` | gauge | avg | sum | process / script / container | Disk Bytes Read | Bytes/s read per consumer. | | `io.write_throughput` | gauge | avg | sum | process / script / container | Disk Bytes Written | Bytes/s written per consumer. | | `io.network.read_throughput` | gauge | avg | sum | process / script / container | Network Bytes Received | Bytes/s received per consumer. | | `io.network.write_throughput` | gauge | avg | sum | process / script / container | Network Bytes Sent | Bytes/s sent per consumer. | | `io.read_latency` | histogram | increase | max | — | Disk Latency | Read op latency (p50/p95/p99). | | `io.write_latency` | histogram | increase | max | — | Disk Latency | Write op latency (p50/p95/p99). | | `io.discard_latency` | histogram | increase | max | — | Disk Latency | Discard/TRIM op latency (p50/p95/p99). | | `io.flush_latency` | histogram | increase | max | — | Disk Latency | Flush op latency (p50/p95/p99). | --- ## NVIDIA GPU {#nvidia-gpu} | Metric key | Type | Temporal | Spatial | Group-by | Chart | Description | |---|---|---|---|---|---|---| | `hw.gpu.utilization` | gauge | avg | avg | — | GPU Utilization | % time GPU actively processing. | | `hw.gpu.process.utilization` | gauge | avg | avg | process / script / container | GPU Consumer | GPU utilization by process. | | `hw.gpu.memory.utilization` | gauge | max | sum | — | GPU Memory Utilization | % VRAM in use. | | `hw.gpu.process.memory.utilization` | gauge | max | sum | process / script / container | GPU Memory Consumer | GPU memory by process. | | `hw.gpu.power` | gauge | avg | sum | — | GPU Power Usage | Current power draw (W). | | `hw.gpu.temp` | gauge | avg | max | — | GPU Temperature | Current GPU temp (°C). | | `hw.gpu.clock.sm` | gauge | avg | avg | — | Clock Frequencies | SM clock (MHz). | | `hw.gpu.clock.mem` | gauge | avg | avg | — | Clock Frequencies | Memory clock (MHz). | | `hw.gpu.clocks_throttle` | gauge | avg | max | `gpu.throttle_reason` | Clocks Throttle Reasons | Fraction of time each throttle reason active. | | `hw.gpu.sm.efficiency` | gauge | avg | avg | — | SM Efficiency | SM efficiency (%). | | `hw.gpu.sm.occupancy` | gauge | avg | avg | — | SM Occupancy | SM occupancy (%). | | `hw.gpu.tensor_util` | gauge | avg | avg | — | Tensor Core Utilization | Tensor Core utilization (%). | | `hw.gpu.pcie.rx` | gauge | avg | sum | — | PCIe Transmission Throughput | PCIe received. | | `hw.gpu.pcie.tx` | gauge | avg | sum | — | PCIe Transmission Throughput | PCIe transmitted. | | `hw.gpu.nvlink.rx` | gauge | avg | sum | — | NVLink Transmission Throughput | NVLink received. | | `hw.gpu.nvlink.tx` | gauge | avg | sum | — | NVLink Transmission Throughput | NVLink transmitted. | | `hw.gpu.tensor_util.dfma` | gauge | avg | avg | — | Tensor Core Breakdown | DFMA (FP64). | | `hw.gpu.tensor_util.hmma` | gauge | avg | avg | — | Tensor Core Breakdown | HMMA (FP16). | | `hw.gpu.tensor_util.dmma` | gauge | avg | avg | — | Tensor Core Breakdown | DMMA (INT8 sparse). | | `hw.gpu.tensor_util.imma` | gauge | avg | avg | — | Tensor Core Breakdown | IMMA (integer). | | `hw.gpu.fp64_util` | gauge | avg | avg | — | FP & Integer Utilization | FP64. | | `hw.gpu.fp32_util` | gauge | avg | avg | — | FP & Integer Utilization | FP32. | | `hw.gpu.fp16_util` | gauge | avg | avg | — | FP & Integer Utilization | FP16. | | `hw.gpu.integer_util` | gauge | avg | avg | — | FP & Integer Utilization | Integer. | | `hw.gpu.dram_bw_util` | gauge | avg | avg | — | DRAM Bandwidth Utilization | % DRAM bandwidth vs max. | | `hw.gpu.hostmem_cache_hit` | gauge | avg | avg | — | L2 Cache Hit Rates | Host memory (PCIe); Grace-coupled only. | | `hw.gpu.peermem_cache_hit` | gauge | avg | avg | — | L2 Cache Hit Rates | Peer GPU memory (NVLink). | | `hw.gpu.dram_cache_hit` | gauge | avg | avg | — | L2 Cache Hit Rates | CPU DRAM (C2C). | | `hw.gpu.hostmem_cache_miss` | gauge | avg | avg | — | L2 Cache Miss Rates | Host memory (PCIe); Grace-coupled only. | | `hw.gpu.peermem_cache_miss` | gauge | avg | avg | — | L2 Cache Miss Rates | Peer GPU memory (NVLink). | | `hw.gpu.dram_cache_miss` | gauge | avg | avg | — | L2 Cache Miss Rates | CPU DRAM (C2C). | | `hw.gpu.c2c.data_rx` | gauge | avg | sum | — | C2C Bandwidth | Chip-to-chip received; Grace/NVL only. | | `hw.gpu.c2c.data_tx` | gauge | avg | sum | — | C2C Bandwidth | Chip-to-chip transmitted. | ### NVIDIA GPU — MIG partitions {#gpu--mig-partitions} Scoped to MIG devices. | Metric key | Type | Temporal | Spatial | Group-by | Chart / usage | Description | |---|---|---|---|---|---|---| | `hw.gpu.memory.utilization` | gauge | max | sum | — | Combined MIG — GPU Memory Utilization | VRAM % for selected/all MIG devices. | | `hw.gpu.utilization` | gauge | avg | avg | — | Combined MIG — GPU Utilization | Active-processing % for MIG devices. | | `hw.gpu.tensor_util` | gauge | avg | avg | — | Combined MIG — Tensor Core Utilization | Tensor Core % for MIG devices. | | `hw.gpu.utilization` | gauge | avg | avg | `gpu.uuid`, `gpu.name`, `host.name` | MIG devices table | "Utilization" column + device discovery. | | `hw.gpu.memory.utilization` | gauge | max | sum | `gpu.uuid`, `gpu.name`, `host.name` | MIG devices table | "Memory" column per MIG device. | | `hw.gpu.tensor_util` | gauge | avg | avg | `gpu.uuid`, `gpu.name`, `host.name` | MIG devices table | "Tensor Util" column per MIG device. | --- ## AMD GPU {#amd-gpu} AMD device metrics are reported per device and are not available per process. Metric availability depends on the GPU and AMD SMI support for that ASIC; without `libdrm`, GPU names and memory metrics are unavailable. | Metric key | Type | Temporal | Spatial | Group-by | Chart | Description | |---|---|---|---|---|---|---| | `hw.amd_gpu.utilization` | gauge | avg | avg | — | GPU Utilization | % time GPU actively processing. | | `hw.amd_gpu.memory.activity` | gauge | avg | avg | — | GPU Memory Activity | How actively the memory controllers service requests (%). | | `hw.amd_gpu.multimedia.utilization` | gauge | avg | avg | — | Multimedia Engine Utilization | Multimedia encode/decode engine utilization (%). | | `hw.amd_gpu.memory.utilization` | gauge | max | sum | — | GPU Memory Utilization | GPU memory currently in use (bytes); reported per device, fleet total is the spatial sum. | | `hw.amd_gpu.power` | gauge | avg | sum | — | GPU Power | Current power usage (mW). | | `hw.amd_gpu.power_limit` | gauge | max | sum | — | GPU Power | Configured power limit (mW). | | `hw.amd_gpu.temp` | gauge | avg | max | — | GPU Temperature | GPU edge temperature (°C), where the ASIC exposes an edge sensor. | | `hw.amd_gpu.clock.graphics` | gauge | avg | avg | — | Clock Frequencies | Current graphics clock (MHz). | | `hw.amd_gpu.clock.mem` | gauge | avg | avg | — | Clock Frequencies | Current memory clock (MHz). | --- ## AWS Neuron {#neuron} | Metric key | Type | Temporal | Spatial | Group-by | Chart | Description | |---|---|---|---|---|---|---| | `hw.process.neuron.memory.utilization` | gauge | avg | sum | process / script / container | Neuron Runtime Memory Usage by process | Runtime memory by process. | | `hw.process.neuron.inference_count` | gauge | avg | sum | process / script / container | Neuron Inferences by process | Successful inferences by process. | | `hw.process.neuron.model_load_count` | gauge | avg | sum | process / script / container | Neuron Model Loads by process | Model loads by process. | | `hw.process.neuron.flop_count` | gauge | avg | sum | process / script / container | Neuron Device FLOP/s by process | Device FLOP/s by process. | | `hw.process.neuron.memory.utilization` | gauge | max | sum | — | Neuron Device Memory Usage | Total device memory usage. | | `hw.neuron.power.utilization` | gauge | max | avg | — | Neuron Device Peak Power Utilization | Peak device power. | | `hw.neuron.core.inference_count` | gauge | avg | sum | — | Neuron Inferences | Total successful inferences. | | `hw.neuron.core.model_load_count` | gauge | avg | sum | — | Neuron Model Loads | Total model loads. | | `hw.process.neuron.flop_count` | gauge | avg | sum | — | Neuron Device FLOP/s | Total device FLOP/s. | --- ## vLLM Optionally scoped to a selected model. | Metric key | Type | Temporal | Spatial | Group-by | Chart | Description | |---|---|---|---|---|---|---| | `vllm:generation_tokens_total` | gauge | rate / avg | sum | process / script / container | Generated Tokens | Output tokens produced. | | `vllm:prompt_tokens_total` | gauge | rate / avg | sum | process / script / container | Prompt Tokens | Input tokens processed. | | `vllm:time_per_output_token_seconds` | histogram | increase | max | process / script / container | Time per output Token | Per-output-token latency (p50/p95/p99). | | `vllm:time_to_first_token_seconds` | histogram | increase | max | process / script / container | Time to first token | TTFT latency (p50/p95/p99). | | `vllm:e2e_request_latency_seconds` | histogram | increase | max | process / script / container | End to end request latency | E2E latency (p50/p95/p99). | | `vllm:num_requests_running` | gauge | avg | sum | process / script / container | Running Requests | In-flight requests. | | `vllm:kv_cache_usage_perc` | gauge | avg | avg | process / script / container | KV Cache Usage | KV-cache memory utilization %. | --- ## Dynamo-Triton {#triton} Optionally scoped to a selected model. | Metric key | Type | Temporal | Spatial | Group-by | Chart | Description | |---|---|---|---|---|---|---| | `nv_inference_count` | gauge | avg | sum | process / script / container | Inferences | Inferences performed (excl. cached). | | `nv_inference_request_success` | gauge | avg | sum | process / script / container | Successful Inferences | Successful requests, all batch sizes. | | `nv_inference_request_failure` | gauge | avg | sum | process / script / container | Failed Inferences | Failed requests, all batch sizes. | | `nv_inference_exec_count` | gauge | avg | sum | process / script / container | Model Executions | Model executions (excl. cached). | | `nv_inference_request_duration_us` | gauge | avg | sum | process / script / container | Request Duration | Cumulative request duration (incl. cached). | | `nv_inference_queue_duration_us` | gauge | avg | sum | process / script / container | Queue Duration | Cumulative queuing duration (incl. cached). | | `nv_inference_compute_input_duration_us` | gauge | avg | sum | process / script / container | Compute Input Duration | Cumulative compute-input duration (excl. cached). | | `nv_inference_compute_infer_duration_us` | gauge | avg | sum | process / script / container | Compute Inference Duration | Cumulative compute-infer duration (excl. cached). | | `nv_inference_compute_output_duration_us` | gauge | avg | sum | process / script / container | Compute Output Duration | Cumulative compute-output duration (excl. cached). | | `nv_inference_pending_request_count` | gauge | avg | sum | process / script / container | Pending Requests | Pending requests awaiting execution per model. | | `nv_model_load_duration_secs` | gauge | avg | sum | process / script / container | Model Load Duration | Model load duration. | | `nv_pinned_memory_pool_used_bytes` | gauge | avg | sum | process / script / container | Pinned Memory Pool Used | Pinned memory pool used. | | `nv_pinned_memory_pool_total_bytes` | gauge | avg | sum | process / script / container | Pinned Memory Pool Total | Total pinned memory pool size. | --- ## SGLang Optionally scoped to a selected model. | Metric key | Type | Temporal | Spatial | Group-by | Chart | Description | |---|---|---|---|---|---|---| | `sglang:generation_tokens_total` | gauge | avg | sum | process / script / container | Generated Tokens | Output tokens produced. | | `sglang:num_requests_total` | gauge | avg | sum | process / script / container | Requests Processed | Requests processed. | | `sglang:prompt_tokens_total` | gauge | avg | sum | process / script / container | Prompt Tokens | Input tokens processed. | | `sglang:gen_throughput` | gauge | avg | sum | process / script / container | Generation Throughput | Tokens/s generation throughput. | | `sglang:time_to_first_token_seconds` | histogram | increase | max | process / script / container | Time to first token | TTFT latency (p50/p95/p99). | | `sglang:e2e_request_latency_seconds` | histogram | increase | max | process / script / container | End to end request latency | E2E latency (p50/p95/p99). | | `sglang:cache_hit_rate` | gauge | avg | avg | process / script / container | Prefix Cache Hit Rate | Prefix cache hit rate. | | `sglang:num_running_reqs` | gauge | avg | sum | process / script / container | Running Requests | In-flight requests. | | `sglang:num_queue_reqs` | gauge | avg | sum | process / script / container | Queued Requests | Requests in waiting queue. | --- ## Host summary cards Metrics behind the host-stats summary cards. Spatial aggregation is `max` (or `sum` depending on the view). | Metric key | Type | Temporal | Spatial | Group-by | Purpose | |---|---|---|---|---|---| | `host.core_count` | gauge | max | max\|sum | — | CPU cores card. | | `host.memory` | gauge | max | max\|sum | — | Memory card. | | `gpu.memory` | gauge | max | max\|sum | — | GPU memory card. | | `gpu.count` | gauge | max | max\|sum | — | GPU count card. | | `amd_gpu.memory` | gauge | max | max\|sum | — | AMD GPU memory card. | | `amd_gpu.count` | gauge | max | max\|sum | — | AMD GPU count card. | | `neuron.memory` | gauge | max | max\|sum | — | Neuron memory card. | | `neuron.device.count` | gauge | max | max\|sum | — | Neuron device count card. | | `neuron.core.count` | gauge | max | max\|sum | — | Neuron core count card. | --- # Multi-GPU Profiling URL: https://docs.zymtrace.com/gpu/multi-gpu ```mdx-code-block import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; ``` # Multi-GPU Profiling Zymtrace is designed from the ground up for multi-GPU, multi-host, multi-workload profiling, regardless of GPU vendor. There is nothing special you need to do: simply [deploy the profiler](/install/profiler/gpu-profiler-quick-start) on every node you want to observe and all data flows into the same backend, ready to be explored as a unified view or sliced by any dimension you care about. ## How It Works The Zymtrace profiler runs as a lightweight, zero-friction agent on each host. When deployed as a **Kubernetes DaemonSet** it is placed on every node in the cluster automatically. For bare-metal or VM deployments you install one profiler binary per host. Either way, every agent sends its data to the same [backend ingest service](/architecture#ingest-service), which stores and indexes it in ClickHouse together with all the OTEL resource attributes that identify where the data came from (host name, pod name, namespace, container, etc.). ```mermaid flowchart TD subgraph NodeA["Node A"] gpuA["GPU 0 · GPU 1 · GPU 2"] profA["zymtrace profiler"] gpuA --> profA end subgraph NodeB["Node B"] gpuB["GPU 0 · GPU 1"] profB["zymtrace profiler"] gpuB --> profB end subgraph NodeC["Node C"] gpuC["GPU 0"] profC["zymtrace profiler"] gpuC --> profC end profA & profB & profC --> backend["zymtrace backend: single unified view"] ``` There are no extra multi-GPU flags on the profiler. The same command you use for a single node works across an entire fleet. Each GPU workload is enabled the same way it would be on a single node: CUDA workloads are profiled via [injection or attach](/install/profiler/profiling-gpu-workloads); no per-node changes are needed when scaling out. Each ROCm workload must be started with the [AMD profiling environment variables](/gpu/amd/gpu-metrics#enabling-amd-gpu-profiling). No additional profiler flag is required when moving from one GPU to several GPUs. :::tip Single backend, any scale Because every profiler agent reports to the same backend, adding more GPU nodes never requires changes to your backend setup. Just deploy the agent on the new host and data appears automatically. A mixed NVIDIA and AMD fleet can use the same backend and the same views. ::: ## Filtering and Grouping Data Once data is flowing in from multiple nodes, use the [filter system](/features/filtering-data) to slice it any way you need. The filter bar is available on every page (Efficiency IQ, Top Functions, Top Entities, Flamegraphs, and Diff) and anything you set persists as you navigate between views. ### Key Attributes for Multi-GPU Workflows | Attribute | Example Value | Use Case | |-----------|---------------|----------| | `host.name` | `gpu-node-42` | Isolate a single physical or virtual machine | | `cluster.name` | `training-cluster-prod` | Scope to an entire cluster | | `gpu.name` | `NVIDIA H100 80GB HBM3`, `AMD Instinct MI300X` | Filter by GPU model (or vendor) across all nodes | | `k8s.namespace.name` | `ml-production` | Scope to a whole Kubernetes namespace | | `k8s.deployment.name` | `vllm-deployment` | Compare all replicas of a deployment | | `k8s.pod.name` | `vllm-server-0` | Focus on one pod across any node it runs on | | `container.name` | `inference-worker` | Drill into a specific container | | `user.tag` | `gpu_type:h100` | Filter by your own custom tags | For the full list of supported filter attributes see [Filtering Data](/features/filtering-data#filter-attributes). ## Fleet-Wide Analysis with Top Entities The [Top Entities](/features/top-entities) view is the natural starting point for fleet-wide GPU analysis. Switch to the **Hosts** grouping to see every GPU node ranked by CPU or GPU resource consumption in a single list. From there you can: - **Spot outliers**: a node consuming far more or far less than its peers is worth investigating. - **Drill into a specific node**: click any host to see its GPU metrics (utilization, memory, power, and more) and navigate directly to flamegraphs or top functions for that node. - **Group by Namespace, Deployment, or Pod**: understand how GPU resources are distributed across teams, services, or replicas. - **Filter by `gpu.name`**: compare like-for-like devices across the fleet, or scope a view to one vendor or device family in a mixed fleet. ## Comparing GPU Nodes with Diff If you want to compare the performance of two GPU nodes, for example an H100 versus an A100, or an MI300X versus an MI325X running the same workload, use the [Diff view](/features/diff). Apply a filter for each node in the two time windows being compared to get a function-level breakdown of what differs, down to the CUDA or HIP kernels and CPU call paths that account for the difference. --- # NVIDIA GPU Metrics URL: https://docs.zymtrace.com/gpu/nvidia/gpu-metrics # NVIDIA GPU Metrics GPU metrics complement profiles by providing high-level visibility into GPU performance, resource utilization, and hardware health. Use metrics to form hypotheses about performance issues, then dive deeper with [GPU profiling](/gpu-profiler-architecture) for detailed analysis: - **Metrics** help you form hypotheses by revealing performance trends and identifying potential bottlenecks - **Profiling** provides detailed CUDA kernel-level analysis to validate hypotheses and pinpoint optimization opportunities Together, they enable a complete GPU performance analysis workflow from hypothesis formation to detailed optimization. :::info Important Usage Notes Metrics are designed for monitoring purposes. If you need deep visibility into your code and how efficiently it runs on GPUs, use [profiling](/gpu-profiler-architecture). A typical workflow is to start from an interesting data point on high or low power or memory consumption, click on it, and navigate to the corresponding GPU profiles for detailed analysis. Note that **Tensor Core**, **SM Efficiency**, and **SM Occupancy** metrics are only available on data center GPUs such as H100s and H200s. ::: ## Enabling GPU Metrics GPU metrics collection is **enabled by default**, so no `--enable-gpu-metrics` flag is required. On NVIDIA hosts, the profiler also needs to locate the NVML library: add `--nvml-auto-scan` to detect it automatically, or `--nvml-path=/path/to/libnvidia-ml.so` if you know the exact path. See the [installation guide](/install/profiler/install-profiler) for Helm, Docker, binary, and systemd instructions. To turn metrics collection off, set `-enable-gpu-metrics=false`. See [Profiler ENV & CLI Args](/install/profiler/profiler-configuration). GPU metrics ## Available GPU Metrics {#available-gpu-metrics} Zymtrace collects GPU metrics across four categories — **Performance** (GPU utilization and per-process consumers, SM efficiency/occupancy, Tensor Core utilization), **Memory** (VRAM utilization and per-process consumers), **Hardware** (power, temperature, clocks), and **Data Transfer** (PCIe, NVLink, and C2C throughput). SM Efficiency, SM Occupancy, and Tensor Core metrics are available only on data center GPUs (H100, H200, etc.). For the complete list — every metric key, type, aggregation, and grouping dimension — see the **[Metrics Reference](../metrics-reference.md#nvidia-gpu)**. ## How GPU Metrics Collection Works Zymtrace uses NVIDIA's NVML (NVIDIA Management Library) to collect GPU metrics. NVML provides a C API for monitoring and managing NVIDIA GPU devices, enabling us to gather real-time performance, utilization, and health data directly from the GPU hardware. ## Using GPU Metrics with GPU Profiles Once GPU metrics are enabled, you can navigate to **Top Entities** and select your GPU host to see the metrics dashboard. This view displays real-time and historical data across all categories, giving insight into patterns in utilization, memory usage, and power consumption. By examining these trends, you can identify anomalies that might indicate performance issues. Clicking on specific data points then allows you to drill down into detailed GPU profiles. Expanded GPU memory consumer view showing process-level breakdown For more granular investigation, the metrics also provide process-level breakdowns. Expanding individual metric widgets shows which processes are consuming the most resources. For example, in our analysis, Hugging Face's [GPU Fryer](https://github.com/huggingface/gpu-fryer) consumes roughly 90% of available GPU memory. This immediately suggests a hypothesis: high memory pressure could be contributing to performance degradation. Metrics like this guide the investigation toward examining memory allocation patterns and kernel execution characteristics for the process in question. GPU flamegraph of a PyTorch ResNet inference script showing Python module frames, ATen operators, the CUDA launch path, a vectorized elementwise kernel with SASS mnemonics, and the selected long_scoreboard stall reason alongside AI optimization recommendations Clicking on the relevant data point, you can analyze the data with GPU profiles. The flamegraph above is scoped to a PyTorch ResNet inference script (`script.name: /root/ava_resnet/ava_scorer_v1.py`) and attributes GPU time through the full launch path: the Python module stack (`Sequential.forward`, `Bottleneck.forward`, `ReLU.forward`), the ATen operators behind it (`at::_ops::relu_`, `clamp_min_`), the CUDA runtime and driver (`cudaLaunchKernel`, `cuLaunchKernel`), and finally the CUDA kernel: ``` at::native::vectorized_elementwise_kernel ``` Below the kernel, the flamegraph continues to the instruction level: SASS mnemonics such as `FSETP`, `S2R`, and `IMAD`, and the PC-sampling stall reasons behind them. The selected stall reason here is `smsp__pcsamp_warps_issue_stalled_long_scoreboard`, accounting for 15.99s (1.17%) of GPU time. It means warps were stalled waiting on a scoreboard dependency for an L1TEX (local, global, surface, or texture) memory operation: the kernel is memory-bound on this path. The built-in AI assistant analyzes the same flamegraph and turns the observation into an actionable recommendation. In this run it flags memory-layout conversion overhead (the `nchwToNhwcKernel` and `nhwcToNchwKernel` cuDNN kernels, roughly 5.7% of total GPU time combined) and suggests running the model in `torch.channels_last` memory format to eliminate the conversions, with an expected end-to-end improvement of 5 to 8%. ## Understanding the Metrics ### Performance Metrics **GPU Utilization** provides the overall percentage of time your GPU is actively processing workloads. This is a key indicator of how well your GPU resources are being utilized. **GPU Consumer** breaks down GPU utilization by process, helping you identify which applications or workloads are consuming the most GPU resources. **SM Efficiency** measures how effectively the Streaming Multiprocessors are being utilized. It indicates the percentage of cycles where the SM has one or more of its assigned warps actively making progress in their execution. Low efficiency might indicate suboptimal kernel configurations or inefficient memory access patterns. **SM Occupancy** shows the percentage of maximum possible threads that are active on the SMs. While higher occupancy often correlates with better performance, this isn't always the case. **Tensor Core Utilization** is particularly important for AI/ML workloads, as it shows how well you're leveraging the specialized hardware designed for these operations. ### Memory Metrics **GPU Memory Utilization** shows how much of your GPU's VRAM is currently in use. Monitoring this metric helps prevent out-of-memory errors and optimize memory allocation strategies. **GPU Memory Consumer** provides a breakdown of memory usage by process, helping identify which applications consume the most memory. ### Hardware Metrics **GPU Power Usage** and **GPU Temperature** are crucial for understanding the thermal and power characteristics of your workloads. These metrics help with capacity planning and thermal management strategies. ### Data Transfer Metrics **PCIe Transmission Throughput** measures data movement between the GPU and system memory, which can become a bottleneck for data-intensive workloads. **NVLink Transmission Throughput** (available on supported hardware) measures high-speed GPU-to-GPU communication, which is critical for multi-GPU workloads. --- # MIG-Aware GPU Profiling URL: https://docs.zymtrace.com/gpu/nvidia/mig-support # MIG-Aware GPU Profiling Zymtrace supports NVIDIA Multi-Instance GPU (MIG) devices. When MIG is enabled on a GPU, Zymtrace automatically detects the MIG topology and profiles each instance independently, with no additional configuration required. ## What is MIG? NVIDIA's [Multi-Instance GPU (MIG)](https://www.nvidia.com/en-us/technologies/multi-instance-gpu/) technology partitions a single physical GPU into up to seven fully isolated instances, each with its own dedicated high-bandwidth memory, cache, and compute cores. MIG is available on Hopper, Blackwell, and Rubin architectures. MIG is commonly used in multi-tenant AI inference clusters where multiple models or users share the same hardware. Each MIG instance operates with full hardware-level isolation, ensuring that workloads on one slice cannot interfere with workloads on another. MIG architecture overview ## How it works Zymtrace detects MIG topology automatically from the host. If MIG is enabled, each instance appears as a separate device in the Zymtrace UI with its own: - **GPU utilization** metrics - **Memory utilization** metrics - **Tensor Core utilization** metrics - **Full-stack GPU flamegraphs** attributed to that specific instance From any MIG instance, you can drill directly into a GPU flamegraph scoped to that slice: CPU call stacks correlated with GPU kernel activity, attributed to the exact MIG profile running your workload. :::tip No configuration needed MIG detection is fully automatic. No code changes, no sidecars, no instrumentation, and no additional flags are required. If MIG is enabled on the host, Zymtrace sees it. ::: ## Requirements - NVIDIA GPU with MIG support (Hopper, Blackwell, or Rubin architecture) - MIG mode enabled on the GPU - [GPU metrics](/gpu/nvidia/gpu-metrics) enabled in the Zymtrace profiler (on by default) ## Enabling MIG profiling MIG support is included automatically when GPU metrics are enabled, which is the default. Follow the standard [GPU metrics setup](/gpu/nvidia/gpu-metrics#enabling-gpu-metrics); on NVIDIA hosts the only flag you may need is NVML discovery (`--nvml-auto-scan` or `--nvml-path`). See the [installation guide](/install/profiler/install-profiler) for Helm, Docker, binary, and systemd instructions. ## Using MIG metrics Once deployed on a node with MIG-enabled GPUs, navigate to **Top Entities** and select your GPU host. Each MIG instance appears as a separate device in the metrics dashboard, showing per-instance utilization, memory, and Tensor Core metrics. Clicking on a specific MIG instance lets you drill down into the GPU flamegraph for that slice, enabling you to identify exactly which kernels or CPU code paths are responsible for performance issues on that particular instance. MIG drill-down view showing per-instance GPU metrics in Zymtrace