Checks/ACCOUNTS_004

NATS Orphaned Exports: What They Mean and How to Fix Them

Severity
Warning
Category
Consistency
Applies to
Account
Check ID
ACCOUNTS_004
Detection threshold
export has no matching import in any account

An orphaned export is an account export with no matching importer in any account. The exporting account is advertising a service or stream subject that nobody is consuming, which means the export serves no purpose and adds configuration clutter that makes your account topology harder to reason about. Insights uses NATS wildcard subject matching to detect these, so an export on orders.> with an import on orders.us.created is correctly recognized as matched.

Why this matters

In a multi-account NATS deployment, exports and imports form the inter-account communication mesh. Each export declares “this account makes subject X available to others” and each import declares “this account wants to consume subject X from account Y.” When an export has no corresponding import, that link is broken — or was never completed.

The immediate risk isn’t operational failure. Orphaned exports don’t cause crashes or data loss. The risk is configuration drift and security surface area. Every export is a potential access point. A stream export on orders.> that nobody imports today could be imported tomorrow by any account with the right activation token — or by any account at all if the export is public. In security-conscious environments, unused exports violate the principle of least privilege: you’re advertising capabilities that nothing needs.

The deeper problem is operational visibility. When an operator audits the account topology — during incident response, capacity planning, or onboarding — orphaned exports create noise. They raise questions: “Is this export supposed to have an importer? Did something break? Is a service down that should be importing this?” Each orphaned export is a small investigation that wastes time. In deployments with dozens of accounts and hundreds of exports, that noise compounds. Cleaning up orphaned exports keeps the account graph accurate and the on-call experience manageable.

Common causes

  • Service retirement without export cleanup. A service that consumed the export was decommissioned, its account was removed or its import was deleted, but nobody updated the exporting account to remove the now-unused export. This is the most common cause — imports and exports are managed independently and often by different teams.

  • Account deletion without dependency review. The importing account was deleted entirely. All its imports vanish with it, but the exporting account’s configuration doesn’t change. The export becomes orphaned silently.

  • Incomplete provisioning. An export was created in advance of the consuming service being deployed. The consuming service was delayed or cancelled, but the export was never rolled back. This is especially common in staged rollouts where the exporting account is provisioned first.

  • Subject rename without configuration update. The subject hierarchy was refactored — orders.processed became orders.fulfilled — and the import was updated to the new subject, but the old export on orders.processed was left in place alongside the new one. Now the old export is orphaned.

  • Copy-paste configuration errors. Account JWTs or server configurations were duplicated from a template or another account, bringing along exports that aren’t relevant to the new account.

How to diagnose

List all exports for an account

In nsc-managed deployments, inspect the account’s exports:

Terminal window
nsc describe account <account_name>

Look at the Exports section. Each export shows the subject, type (stream or service), and whether it’s public or requires activation tokens.

To list exports across all accounts:

Terminal window
nsc list exports

Check for matching imports

For each export, verify that at least one other account has a corresponding import:

Terminal window
nsc list imports

Compare the import subjects and source accounts against the export list. An export with no matching import — accounting for NATS wildcard matching — is orphaned.

Query account information from the server

If you have server access, check active account information:

Terminal window
nats account info

This shows the account’s active imports and exports as the server sees them. If an export appears here but you can’t find a corresponding import in any account, it’s orphaned.

Audit the full account topology

For a comprehensive view across all accounts, use the server’s account statistics:

Terminal window
curl -s http://localhost:8222/accstatz | jq '.account_statz[].acc'

Then iterate through each account to map the complete export-import graph. In large deployments, scripting this audit is worth the upfront investment.

How to fix it

Immediate: verify the export is truly unused

Before removing any export, confirm that no account depends on it. An export might appear orphaned if the importing account is temporarily offline or if the import uses wildcard subjects that don’t match a simple string comparison.

Check if any subscriptions exist on the exported subject:

Terminal window
nats server report connections --subscriptions

If there are active subscriptions on the exported subject from another account, the export is not truly orphaned — the import may be configured through a different mechanism or the check may have a wildcard matching edge case.

Short-term: remove the orphaned export

Once confirmed unused, remove the export:

JWT/nsc mode:

Terminal window
# Remove a stream export
nsc delete export --account <account_name> --subject "orders.processed"
# Remove a service export
nsc delete export --account <account_name> --subject "api.orders.>" --service
# Push the updated account JWT
nsc push -a <account_name>

Server config mode:

Remove the export from the account’s exports block in the server configuration:

1
accounts {
2
orders_service {
3
exports = [
4
# Remove the orphaned export line
5
# {stream: "orders.processed"}
6
{stream: "orders.fulfilled"} # Keep active exports
7
]
8
}
9
}

Then reload:

Terminal window
nats server config reload <server-id>

Long-term: prevent orphaned exports from accumulating

Implement export-import lifecycle coupling. When a service is decommissioned, include export cleanup in the decommission checklist. If your infrastructure is managed through CI/CD, add validation that flags orphaned exports before merge:

#!/bin/bash
# CI check: verify all exports have at least one import
EXPORTS=$(nsc list exports -a <account> --json | jq -r '.[].subject')
for subj in $EXPORTS; do
IMPORTERS=$(nsc list imports --json | jq -r --arg s "$subj" '.[] | select(.subject == $s) | .account')
if [ -z "$IMPORTERS" ]; then
echo "ERROR: Orphaned export on subject: $subj"
exit 1
fi
done

Document export purposes. Maintain a manifest or use account description fields to record why each export exists and which account(s) should be importing it. When the answer to “who imports this?” requires archaeology, exports get orphaned.

Periodic audits. Schedule quarterly reviews of the account export-import graph. Synadia Insights automates this by evaluating all exports against all imports using NATS wildcard matching every collection epoch, flagging orphaned exports as they appear rather than waiting for a manual audit.

Frequently asked questions

Are orphaned exports a security risk?

They can be. A public export with no current importer is still available for any account to import. If the exported subject carries sensitive data, the unused export is an unnecessary access point. Private exports are lower risk since they require activation tokens, but the token could still be generated and distributed. The safest approach is to remove exports that serve no active purpose.

Does removing an export affect existing connections?

Removing an export and pushing the updated JWT (or reloading config) takes effect on the server immediately. Any account that currently imports the export will lose access to the subject. If you’ve confirmed no imports exist, there’s no impact. If an import does exist that you missed, the importing account’s subscriptions on that subject will stop receiving messages.

How does Insights detect orphaned exports with wildcard subjects?

Insights uses NATS wildcard matching semantics — not simple string comparison — when evaluating whether an import matches an export. An export on orders.> with an import on orders.fulfilled is matched correctly. An export on orders.processed with an import on orders.> also matches. This eliminates false positives from wildcard mismatches that a naive string comparison would produce.

Should I remove exports that were created for future services?

If the service has a defined timeline and is actively being developed, keeping the export is reasonable — but document it. If the service is speculative or indefinitely delayed, remove the export. You can always re-add it when the consuming service is actually ready to deploy. Keeping unused exports “just in case” is how configuration drift accumulates.

Proactive monitoring for NATS orphaned export with Synadia Insights

With 100+ always-on audit Checks from the NATS experts, Insights helps you find and fix problems before they become costly incidents.
No alert rules to write. No dashboards to maintain.

Start a 14-day Insights trial
Cancel