What you’ll build
You already move fast with Nhost: Postgres, real-time GraphQL, Authentication, Storage, and Serverless Functions without touching servers. Now you can add observability and monitoring that is just as fast. In this guide, you’ll set up a vendor-neutral OpenTelemetry pipeline so your Nhost GraphQL API and Functions emit traces, logs and metrics to your OpenTelemetry Collector and visualization tool of choice. You’ll see one coherent, end-to-end trace that flows across GraphQL resolvers, Actions, Event Triggers, Functions, Postgres timings, Auth webhooks, Storage operations, and even Bring Your Own Containers (BYOC) services—without managing any infrastructure on your own.
The result: reliable Nhost monitoring and performance monitoring with service graphs that make sense, alerting that catches real problems, and dashboards that explain “why” when latency spikes or error rates creep up.
Reference architecture
The architecture is intentionally simple and portable. Nhost services export OpenTelemetry signals over OTLP/HTTPS to an internet-accessible Collector that fans out to your preferred backends (Grafana Tempo/Loki/Mimir, Honeycomb, Datadog, New Relic, or any OTEL-compatible destination).
- GraphQL API, Functions, and BYOC services send traces, logs and metrics via OTLP/HTTP(S).
- An OpenTelemetry Collector receives, batches, samples, scrubs, and routes signals.
- Dashboards and alerts run in your chosen tool, fed by the Collector’s exporters.
Signals and flow
Use W3C Trace Context so requests stitch across every hop. The traceparent and tracestate headers start in the GraphQL API, pass through Actions or Event Triggers to Serverless Functions, and return with enriched spans. Database timings appear in GraphQL resolver spans, so the same distributed trace shows client request, resolver latency, HTTP calls to Functions, and query timing inside Postgres.
Required Nhost touchpoints
You do not need to provision or manage any Nhost servers. Configure environment variables in the Nhost Console for the GraphQL API and for Serverless Functions (Node/TypeScript). Point them to your Collector’s public OTLP endpoint, add a token header, and define resource attributes. Redeploy, and signals flow. That’s it—observability and monitoring without cluster setup.
Prerequisites
- An OpenTelemetry Collector reachable over the internet (public URL) with a token or API key for auth.
- Outbound HTTPS allowed from your Nhost project (default). Validate TLS and firewall rules on your Collector.
- Your Nhost project subdomain/ID (for resource attributes and filters).
- Access to your telemetry backend (Grafana, Honeycomb, Datadog, etc.) to verify data and build dashboards.
If you are still evaluating backend platforms and how they compare for tracing, auth, and pricing, see the open-source comparison: Nhost vs Supabase vs Appwrite (2026): Open-Source Backend Comparison for Postgres, GraphQL, Auth, and Pricing.
Enable OpenTelemetry in the GraphQL API
Turn on tracing and export to OTLP using environment variables in the Nhost Console. The GraphQL engine supports OpenTelemetry natively, so you do not add code or sidecars. After configuring, trigger a redeploy of the GraphQL service and confirm traces appear in your backend.
Exporter endpoint and sampling
- Set your OTLP/HTTP or OTLP/gRPC endpoint to your Collector URL (HTTPS recommended).
- Provide authentication via headers (for example, an Authorization bearer token) in the exporter configuration.
- Start with parent-based 10–20% sampling in the Collector or exporter. This balances visibility with cost and keeps performance monitoring representative. For incident triage, you can temporarily raise sampling to 100% to capture every trace.
Resource attributes
- service.name: nhost-graphql
- deployment.environment: dev, staging, or prod
- nhost.project.id: your Nhost project ID or subdomain
- Optional: service.version from Git SHA, and cloud.region if you run multi-region
These attributes power clean service graphs and let you filter Nhost monitoring views by project and environment without cardinality explosions.
Instrument Serverless Functions (Node/TS)
For Functions, add the OpenTelemetry Node SDK once at startup and enable auto-instrumentation for HTTP servers, fetch/axios, and popular libraries. Configure the SDK to export traces, logs and metrics to the same Collector as GraphQL. Because Functions run in a managed environment, set exporter details and resource attributes via Nhost environment variables, not hard-coded values.
OTLP exporter config
- Use OTLP over HTTPS with a token in headers for secure delivery.
- Mirror GraphQL settings for deployment.environment and nhost.project.id to keep dashboards and alerts unified.
- Prefer batch exporters with reasonable flush intervals to minimize overhead.
Context propagation
Forward and respect the traceparent header. When GraphQL invokes Actions or Event Triggers, ensure the Function reads the trace context and continues the parent trace. When Functions call back into GraphQL or other services, include the same W3C headers so spans link bi-directionally. This is the key to end-to-end observability and monitoring that tells one story instead of scattering signals across isolated views.
End-to-end trace: GraphQL -> Action -> Function -> Postgres
To validate setup, create a GraphQL Action that triggers a Function which queries Postgres through the GraphQL engine. In your trace, you should see:
- The incoming GraphQL operation with operationName and variables size
- An HTTP client span from GraphQL to the Function (or an Action span if proxied)
- Function spans for handler execution, external calls, and cache checks
- Resolver/database spans showing Postgres timings, row counts, and errors if any
When that single trace shows the resolver and downstream Function time together, your performance monitoring workflow becomes straightforward: identify outliers, inspect spans, and pin down whether time is spent in validation, business logic, network, or the database.
Database metrics and slow queries
You can extract a lot of value from GraphQL resolver spans. They include query timings and labels like operationName and table/field context. Use them to chart p95 latency by operation, error rate by mutation, and throughput across environments. This keeps Nhost monitoring focused on what users experience rather than raw query text.
Optional postgres receiver
If you maintain a secure read-only connection outside of Nhost, add the Collector’s Postgres receiver to scrape pg_stat* views. Correlate with traces using db.system=postgres and align service.name and nhost.project.id. Use this for capacity planning and query planning trends; keep high-cardinality fields like db.statement sanitized or truncated to avoid metric blowups. If you prefer not to manage any DB connections, rely on resolver timings—they are often sufficient for practical performance monitoring.
Authentication and Storage signals
Instrument Auth webhooks and Storage handlers so they emit traces, logs and metrics. This lets you catch 401/403 spikes, token issuance latency, and upload/download slowdowns quickly. When a user flow fails, you want immediate visibility across signup, login, avatar upload, and profile update in a single distributed trace.
For deeper guidance on securing agents and services with JWTs, see How to Add OAuth2 and JWT Authentication to Your AI Agents (Step-by-Step Guide). The same token practices apply to webhook auth and cross-service calls, and your logs and metrics should reflect token validation outcomes.
Metrics to emit
- Auth: counters for signups, logins, refreshes; error counters by status (401, 403); latency histograms for token issuance and webhook processing
- Storage: counters for uploads/downloads; bytes_in/bytes_out; latency histograms by object size bucket; error counters by status
- Sessions: gauge for active sessions if available, counters for session starts/ends
Bring your own containers (polyglot services)
When you deploy BYOC services, run an OTEL Collector sidecar per service or embed language SDKs (Go, Python, Rust). Keep resource attributes aligned with Nhost components so dashboards remain cohesive:
- service.name: meaningful and stable per service
- nhost.project.id: same as GraphQL and Functions
- deployment.environment: dev/staging/prod
This standardization ensures Nhost monitoring spans all your services regardless of language or framework.
Internal service calls
Ensure HTTP and gRPC clients propagate W3C headers. With consistent propagation between BYOC, Functions, and GraphQL, your traces remain contiguous—even for multi-hop calls and retries. Add error attributes and retry metadata to spans so debugging intermittent failures doesn’t require guessing.
Logs and metrics that matter
Emit structured JSON logs from Functions and BYOC services and route them through OTEL logs. Include trace_id and span_id in each log record. With this, a production error log jumps straight to the exact trace that captured the failure, eliminating context-switching. Your logs and metrics should tell the same story as your traces.
Low-cardinality metrics
- request_duration (histogram) with labels: service.name, deployment.environment, operationName (for GraphQL)
- error_rate (counter or ratio) with labels: service.name, status_class (2xx/4xx/5xx)
- cold_start (counter) for Functions to track warmup impact
- queue_time (histogram) when using queues; measure time-to-start vs time-in-handler
- GraphQL resolver latency (histogram) by operationName and top-level field, not by full query text
Favor stable labels and bucketing to avoid metric cardinality explosions. Your goal is actionable observability and monitoring, not an unbounded time series bill.
Dashboards and alerts for Nhost stacks
Pre-build a few focused dashboards so engineers get answers in one view:
- GraphQL: p95 latency by operationName; 5xx rate; top slow operations; request volume and cache hit/miss if applicable
- Functions: error budget burn; cold start counts and percent; handler p95; external call latency
- Auth/Storage: signup/login success rate, token issuance latency, 401/403 spikes; upload/download p95 and egress volume
- Postgres (optional): lock wait time, buffer hit ratio, top tables by IO, correlated with resolver spans
SLOs and alerts
- Alert when GraphQL p95 exceeds a threshold for a sustained window (e.g., 5 minutes) to avoid noise.
- Error budget burn alerts for Functions: fast-burn for severe incidents, slow-burn for steady regressions.
- Signup/login failure spikes: alert on ratio changes, not just raw counts.
- Storage egress anomalies: detect sudden jumps in bytes_out or 5xx on downloads.
Local development with the Nhost CLI
Run the same stack on your laptop and preview traces before merging. Start a local Collector and point your local Nhost services at its OTLP endpoint using the same environment variable keys as production. This lets you tune sampling, verify context propagation, and align resource attributes early. For a step-by-step local workflow, see Local Development with the Nhost CLI: Run Postgres, GraphQL, and Auth in Containers (BYOC Step-by-Step).
CI/CD and secrets management
- Commit OpenTelemetry configuration templates to version control (document resource attributes and naming).
- Inject OTLP endpoints and tokens via Nhost environment variables per environment (dev/staging/prod).
- Use different sampling strategies per environment: 100% in dev, 20% in staging, and 5–20% in prod depending on traffic.
- Pin service.version to Git SHA for rollbacks and deployment diffing in dashboards.
Troubleshooting
Most issues fall into three buckets: no data, broken trace joins, or high cardinality. Here’s how to resolve each quickly.
No traces arriving
- Verify the OTLP URL is correct (protocol, path, and port). Prefer HTTPS with valid TLS certs.
- Confirm the token/header name matches what the Collector expects.
- Temporarily raise sampling to 100% to rule out sampling as the cause.
- Check Collector logs for rate limits, auth failures, or bad content-type.
Broken joins
- Ensure traceparent is forwarded from GraphQL to Functions (Actions/Event Triggers) and from Functions to any downstream calls.
- Use consistent service.name per component; do not append dynamic suffixes.
- Confirm your backend isn’t rewriting headers or stripping W3C fields.
Cardinality control
- Do not store raw db.statement values as high-cardinality attributes; prefer operationName and table/field summaries.
- Truncate large attribute values in the Collector and cap label sets for metrics.
- Whitelist a minimal set of resource attributes: service.name, nhost.project.id, deployment.environment, service.version.
Next steps
- Add synthetic probes for critical routes and correlate with GraphQL resolver latency.
- Define SLOs per service, wire alerts to Slack or PagerDuty, and document runbooks.
- Roll out OpenTelemetry to all BYOC services and standardize resource attributes across languages.
- Continuously review dashboards for signal-to-noise and refine sampling based on incident learning.
Why Nhost? Because you get a complete, production-ready backend—Postgres, GraphQL, Authentication, Storage, Serverless Functions, and BYOC—plus a straightforward path to end-to-end OpenTelemetry. Set a few environment variables in the Console, redeploy, and your observability and monitoring are live with clean traces, logs and metrics. No DevOps detours. No infrastructure to babysit.
Conclusion
With a vendor-neutral OpenTelemetry pipeline, you can achieve real Nhost monitoring in hours, not weeks. Tie GraphQL, Functions, Postgres timings, Auth, Storage, and BYOC into one distributed trace. Build dashboards focused on user-facing outcomes, keep metrics low-cardinality, and alert on what truly matters. That’s how fast-moving teams ship confidently without getting slowed down by infrastructure.
Ready to see it in action on your stack? Get started for free.
Frequently Asked Questions
Do I need to host my own OpenTelemetry Collector, or can I point Nhost directly to a vendor’s OTLP endpoint?
You can do either. Many vendors expose a public OTLP endpoint with token-based auth, so Nhost services can export directly. Others recommend placing your own Collector in front to handle sampling, scrubbing, and routing to multiple backends. Start with a managed vendor endpoint for speed; introduce your own Collector later if you need advanced control.
How do I enable OpenTelemetry on the Nhost GraphQL API if I don’t control servers?
Use the Nhost Console to set the GraphQL engine’s OpenTelemetry environment variables: the OTLP endpoint, headers for auth, and resource attributes like service.name and deployment.environment. Redeploy the GraphQL service. No server access or sidecars are required.
Can I get database-level metrics from Postgres on Nhost, or should I rely on GraphQL resolver timings?
Resolver timings are usually sufficient for actionable performance monitoring because they reflect what users experience. If you operate a read-only Postgres connection elsewhere, you can scrape pg_stat* via the Collector’s postgres receiver and correlate with traces using db.system=postgres. If you prefer zero infrastructure, stick with resolver spans and operationName-based dashboards.
How do I correlate serverless function logs with GraphQL traces in one view?
Emit structured JSON logs from Functions that include trace_id and span_id and route them through OTEL logs. Ensure Functions propagate the traceparent header from GraphQL. In your backend, use trace-based log correlation to jump from an error log to the exact distributed trace.
What is the performance overhead of enabling OpenTelemetry on a production Nhost project?
With batching and 10–20% parent-based sampling, overhead is typically low single digits of CPU and memory. Network overhead scales with sampling and span size. Start conservative, measure p95 and error rates, and adjust sampling as needed. For incident response, it’s safe to temporarily raise sampling to 100% to capture full fidelity.
💬 Contact Us
Have a question or want to learn more? Send us a message!