Track executions across instances

Hi everyone,

I’m running multiple n8n instances on Google Cloud Run using a single license key.

What’s the best way to track usage per deployment/tenant (executions, active workflows, API usage, etc.)?

Hi @rgrzesk
I think the best way is to use the insights:

But if you want multiple instances of data into a single frame, then I would recommend this:

I have used it, and it works all the time.
Als,o you can just call the N8N API:

GET /api/v1/executions?status=success&limit=250
If you are not on the n8n cloud, the Ud metrics option is the best.

Does this help, @rgrzesk ?

For tracking across multiple Cloud Run instances into one place, the API polling approach won’t scale well - each instance has its own API endpoint and there’s no unified view. A cleaner pattern is to add a dedicated “execution logger” workflow to each instance: it runs on a schedule, hits GET /api/v1/executions with a time window, adds an instance_id tag, and posts the results to a shared Postgres table or Google Sheet. Then you have one central place to query usage across all instances. You can include the Cloud Run service name as the instance identifier via an environment variable injected at deploy time.

This one sounds really nice, but the problem is that I don’t and won’t have access to every instance. I can orchestrate it, but cannot be a user.
I would need to create such predefined workflow when deploying, but I guess it’s not possible to do that easily. The only way is to somehow use public available data. Is it possible to use /metric endpoint? Will I get enough data I could use?

If you do not have API/user access to each instance, I would treat `/metrics` as a partial infrastructure signal, not as a complete usage model.

It can help with questions like “is this instance alive, how busy is it, are executions failing more than usual”, but it usually will not give you clean business attribution like tenant usage, active workflow count per customer, or billable API calls unless you designed the deployment around those labels from the beginning.

For your case, because you can orchestrate the deployment, I would push the tracking boundary into the deploy template:

- give every Cloud Run service a stable `deployment_id` / `tenant_id` label

- enable metrics/log export at deploy time, not after the customer starts using it

- make Cloud Run logs include the same deployment label

- if possible, pre-install one small internal logging workflow during provisioning

- if workflow access is not possible, at least collect instance health, execution totals, failures, latency and restart/error signals centrally

The important part is that the ID must exist outside n8n too. If each instance emits metrics but they arrive without a stable deployment label, you will still end up with a global pile of numbers that is hard to reconcile.

So I would use `/metrics` for operational monitoring, but not depend on it alone for tenant/customer reporting. For usage reporting I would want either a predefined logger workflow, API access, or deployment-level labels that your collector adds before data lands in the central store.

Thanks, that makes sense.
How about using the DB as source of truth for historical/license usage? Would that make sense, does it keep the data I need?
Checking by execution_entity and counting all not marked as manual?

Hi @rgrzesk

there is another approach for your setup which is OpenTelemetry tracing. Since you control deployment, set these env vars during provisioning:

N8N_OTEL_TRACE_ENABLED=true
N8N_OTEL_TRACE_EXPORTER=otlp
OTEL_EXPORTER_OTLP_ENDPOINT=https://your-central-collector:4318

Every execution emits a workflow.execute span with the execution mode, status, workflow ID, and the instance’s unique n8n.instance.id. Point all instances to one collector (Jaeger, Grafana Tempo, etc.) and you get per-instance, per-execution tracking with mode filtering. No DB access needed, no pruning risk, standard protocol, and it’s configured entirely at deploy time which fits your constraints perfectly.

For the DB route as a fallback: yes, execution_entity with mode != 'manual' works, but be aware that pruning deletes these records based on EXECUTIONS_DATA_MAX_AGE. Aggregate into your own store on a shorter schedule than the pruning window.

Let me know if it helps :crossed_fingers:

Nice one!
OL is a good solution for already new instances. Are we somehow able to get historical data as well?

OTel only captures from the moment it’s enabled, no retroactive traces.

For historical data on existing instances, two options since you have DB access:

  1. execution_entity table: query mode != 'manual' for production counts. Only as far back as pruning allows.

  2. Insights tables: n8n stores compacted insights data separately from execution_entity, retained for up to 365 days by default (N8N_INSIGHTS_MAX_AGE_DAYS). This survives execution pruning. Check the insight_* tables in your Postgres schema for aggregated historical counts.

For anything older than what’s in the DB, it’s gone.

Hi!
I had a quite break from the topic, but I am coming back :slight_smile:
Nice that from version 2.27.0 I can also configure OpenTelemetry in the UI.
However I have two questions:

  1. Can I hide these options? Because it reveals API key of the opentelemetry system.
  2. At which execution.mode I should look for in OTL? I mean - which of them are billable? Also !manual ones?

EDIT - for 2. question - I just noticed there is already a flag :slight_smile: N8N_OTEL_TRACES_PRODUCTION_ONLY

There’s no built-in toggle for hiding the OTel API key field in Settings — it’s a known gap, tracked as a feature request here: OpenTelemetry visible UI . Until that ships, the workaround is restricting who can see Settings via RBAC/project roles, or setting OTel env vars directly (N8N_OTEL_*) instead of the UI so the key never renders there.

On execution.mode: only production-triggered runs count toward your billable quota (webhook, schedule, poll-with-data triggers). ‘manual’ executions never count, and neither do sub-workflow calls, error-workflow runs, or empty polls. So filter your OTel/insights query on mode != ‘manual’ as houda_ben suggested, and you’re already looking at the right billable set.

What is the integrated mode? Does it also count?
How sub-workflows are visible then if they don’t count - as manual?

“integrated” is the mode set when a workflow runs as a sub-workflow called via the Execute Workflow node, and it does not carry the parent’s manual/production distinction on its own execution record. For billing, sub-workflow calls are counted toward the parent’s quota only if the parent run itself was production-triggered: a production parent triggers billable integrated child runs, a manual parent’s integrated child runs stay non-billable. So filter on the parent’s mode, not the sub-workflow’s own mode field, when reconciling counts in OTel/Insights.

Shameless plug for LumaTrack, we can do this pretty easily with our verified community node, OTel, MCP, API, etc.

ok, so if I understand you correctly if we have a production execution that triggers 5 different sub-workflows and those 5 sub-workflows run (eg. conditions are met) we pay only ONCE for the main production execution?

@rgrzesk Yes, that’s correct. Billing keys off the parent workflow’s execution mode, not each sub-workflow’s individually. So if the main workflow is triggered in production mode (webhook/schedule/poll) and it calls 5 sub-workflows via Execute Workflow, those child runs execute in “integrated” mode and are not billed separately, you’re only charged for the one parent production execution. This only holds if the sub-workflows are invoked as children of that same run; if you trigger any of them independently via their own production trigger, that run is billed on its own.