Whatβs included
Install Nango
Nango ships as a single Docker image. To install it on a VM:Core configuration
Set these as environment variables in the.env file, or directly in your hosting provider.
Instance URL and callback URL
<INSTANCE-URL>/oauth/callback. Register this URL with your OAuth providers, not api.nango.dev.
If you front one Nango process with two ingresses (a public host for OAuth callbacks and webhooks, an internal host for the dashboard), set:
/ keeps dashboard API requests on whichever host served the page. Callback and webhook URLs still use NANGO_SERVER_URL. Set an absolute URL instead if the dashboard is hosted separately from the API.
Encryption key
Set an encryption key so credentials, API keys, and app secrets are encrypted at rest. Generate a 256-bit base64-encoded key:NANGO_ENCRYPTION_KEY:
Connect UI
To enable connect UI:<INSTANCE-URL>:3009.
Hosting under a base path
Connect UI works under any base path, for example behind a reverse proxy that routes services by path. SetNANGO_PUBLIC_CONNECT_URL to the full URL including the path:
/nango/connect/assets/app.js must reach Connect UI as /assets/app.js.
Connect UI is a single-page app: the server must respond with index.html for any path that isnβt a built asset, including the base path without a trailing slash. Nangoβs static file server handles this out of the box; if you serve the built dist from your own static host, enable its SPA fallback.
Production data stores
The Postgres and Redis bundled with Docker Compose use local container storage and are not appropriate for production. Point Nango at external instances before you go live.Postgres
Connect Nango to an external Postgres database by setting the database environment variables:NANGO_DATABASE_URL must be URL encoded.
Nango is incompatible with connection poolers using
pool_mode=transaction. Use a direct database connection or configure the pooler to use a different mode.Redis
Connect Nango to an external Redis (or Valkey) with either a full URL or discrete variables:rediss:// scheme (or discrete variables, which default to TLS) to enable in-transit encryption.
IAM / short-lived token authentication
Managed Redis with IAM authentication (for example GCP Memorystore for Valkey) uses a short-lived token as the password and requires the token to be refreshed before it expires. Instead of a staticNANGO_REDIS_AUTH, point Nango at a file that an external process (such as a sidecar) keeps up to date:
rename() it into place β so a reconnect never reads a half-written token and fails authentication. When NANGO_REDIS_AUTH_TOKEN_FILE is set, do not embed credentials in NANGO_REDIS_URL.
Logs
Nango stores execution logs and powers the logs UI with either Elasticsearch or OpenSearch. To keep free self-hosted deployments lighter, this stack is optional. To enable logs:- Host an Elasticsearch or OpenSearch cluster.
- Set
NANGO_LOGS_ENABLED=true. - Configure the relevant
NANGO_LOGS_ES_*environment variables (these apply to both providers).
- Local: uncomment the service in
docker-compose.yamland rundocker-compose up. - Elastic Cloud: use elastic.co.
- Render: deploy an Elasticsearch instance with Render.
NANGO_LOGS_ENABLED is false, logs are sent to stdout and can be viewed in your host logs.
Secure your instance
Secure the dashboard
By default, the dashboard of your Nango instance is open to anyone who can access your instance URL. You can secure it with Basic Auth by setting the following environment variables and restarting the server:Proxy base URL override hardening
The proxy can send authenticated requests to external APIs. Some proxy calls accept a base URL override (HTTP headerBase-Url-Override, SDK baseUrlOverride, or an integration custom.baseUrl) to target a host that differs from the providerβs default API base URL.
Because the proxy makes outbound HTTP requests from your Nango server, a caller with permission to use the proxy could use an override to reach hosts that were not meant to be exposed β such as cloud metadata services or localhost on the host making the request. This is a classic SSRF risk.
By default, Nango keeps base URL override enabled and blocks override targets and redirect hops whose hostnames match a built-in denylist (cloud metadata and loopback addresses). Configure these environment variables and restart the server after changes:
Operator guidance:
- Production: keep the default denylist. Add environment-specific hosts if your deployment exposes additional internal endpoints.
- Legitimate localhost overrides (dev only): set
NANGO_PROXY_BASE_URL_OVERRIDE_DENYLIST='[]'to restore fail-open behavior. - No overrides at all: set
NANGO_PROXY_BASE_URL_OVERRIDE_ENABLED=false.
Outbound URL policy (DNS rebinding, private IPs, redirects)
Beyond the hostname denylist, Nango routes every outbound connection through DNS-pinning agents that re-validate the resolved IP address β closing DNS-rebinding and redirect-to-internal-address SSRF holes. The behavior is controlled byNANGO_OUTBOUND_URL_POLICY, a JSON object (applied on top of the denylist).
The mode field selects how hostnames are filtered:
denylist(default): every destination is reachable except hostnames on the denylist (plus the IP-based protections below). This is the out-of-the-box mode, since the denylist always carries the secure defaults.allowlist: only hostnames inallowlistare reachable (a leading.matches subdomains); everything else is rejected. The denylist and the IP-based protections still apply on top, so allowlist is strictly more restrictive β a listed hostname that resolves to a blocked IP is still rejected.permissive: no hostname-based filtering (the IP-based protections below still apply). You only reach this mode deliberately β either by emptying the denylist with no explicit mode, or by settingmode: "permissive".
The IP-based protections apply in every mode, including
permissive. Loopback (127.0.0.0/8, ::1) and unspecified addresses are always blocked; private/RFC1918/CGNAT addresses (blockPrivateIps) and link-local addresses β including the cloud-metadata IP 169.254.169.254 (blockLinkLocal) β are blocked by default.The denylist is never empty by default β it is always seeded with the secure defaults (localhost, metadata.google.internal, 169.254.169.254, β¦). It only becomes empty if you explicitly opt out: either NANGO_PROXY_BASE_URL_OVERRIDE_DENYLIST='[]' (which then selects permissive) or mode: "permissive". Even then, only hostname string matches such as localhost are dropped β literal or resolved internal IPs stay blocked unless you also set blockPrivateIps/blockLinkLocal to false.
Each resolved address (including on every redirect hop) is validated; in
allowlist mode, only listed hostnames are reachable regardless of IP.
The same DNS-pinning protection also covers OAuth/token flows (token exchange, refresh, AWS STS, and JWT-bearer token endpoints). These use a separate overlay, NANGO_OUTBOUND_URL_POLICY_OAUTH, applied on top of NANGO_OUTBOUND_URL_POLICY but with the same JSON shape. The only difference is the default: blockPrivateIps is false for OAuth so integrations whose token endpoints live on a private network keep working. Loopback and unspecified addresses stay blocked regardless. Link-local/metadata addresses (including the cloud-metadata IP 169.254.169.254) stay blocked by default through blockLinkLocal, but that check is disabled if you set blockLinkLocal:false. Lock OAuth down to public hosts only by setting blockPrivateIps:
Custom websockets path
The Nango server serves websockets from/ by default for use by @nangohq/frontend during the API auth flow.
To isolate websockets from the dashboard, set NANGO_SERVER_WEBSOCKETS_PATH:
websocketsPath when initializing the Nango object in the @nangohq/frontend SDK:
websocketsPath only needs to be configured for direct nango.auth() calls. If Nango is served behind a reverse proxy under a sub-path, direct nango.auth() needs the full public path in websocketsPath (e.g. /<API-BASE-PATH></YOUR-WEBSOCKETS-PATH>) because the SDK resolves it from the origin, while Connect UI composes the public path automatically from the apiURL base path and NANGO_SERVER_WEBSOCKETS_PATH.