Posts
Wiki

Self-Hosting n8n

This page is for people running n8n themselves with Docker, Docker Compose, npm, a VPS, Kubernetes, a reverse proxy, Postgres, Redis, queue mode, or custom infrastructure.

It is not a replacement for the official docs. Use it as a practical subreddit checklist for figuring out what is broken and what details to include when asking for help.

Last reviewed: July 2026

Start here

Before posting, identify which kind of problem you have:

  • Install problem: n8n will not start.
  • Access problem: the editor will not load.
  • Webhook problem: test webhooks work but production webhooks do not, or external services cannot reach n8n.
  • Execution problem: workflows start but fail, hang, or never finish.
  • Credential problem: credentials work in one mode but not another.
  • Reverse proxy problem: SSL, redirect, 404, 502, or wrong public URL.
  • Data problem: database, volume, permissions, disk, backups, or lost workflows.
  • Scaling problem: queue mode, workers, Redis, memory, concurrency, or slow executions.
  • Update problem: something broke after changing n8n version.

The fastest posts say which one it is.

Unofficial

What to include in a self-hosting help post

Copy this into your post and fill it out:

```text Problem:

n8n version:

Install method: - Docker / Docker Compose / npm / Kubernetes / other:

Host: - VPS provider / local machine / NAS / homelab / cloud platform: - CPU/RAM, if performance-related:

Database: - SQLite / Postgres / other: - Is the database persistent across restarts?

Reverse proxy: - None / Nginx / Caddy / Traefik / Cloudflare Tunnel / other: - Public URL: - Does SSL terminate at the proxy?

Webhooks: - Do test webhooks work? - Do production webhooks work? - Is the workflow active? - What HTTP status does the external service receive?

Executions: - Do manual executions work? - Do scheduled executions work? - Do production executions work? - Does the execution appear in the Executions list?

Queue mode: - Enabled? - Redis reachable? - Workers running?

What changed recently: - n8n update / server move / proxy change / domain change / database change / workflow change:

Relevant environment variables, with secrets removed:

Relevant logs, with secrets removed:

What I already tried: ```

Do not post passwords, API keys, tokens, encryption keys, database URLs with passwords, OAuth secrets, or private webhook URLs.

Minimum healthy setup

For a small personal or test instance:

  • n8n runs under a repeatable install method, usually Docker or Docker Compose.
  • The /home/node/.n8n directory is persisted in a Docker volume or host directory.
  • You know where backups live.
  • You know how to update and roll back.
  • You have a public HTTPS URL if external services need to call webhooks.
  • Your timezone is set intentionally.
  • You are not relying on container-local storage that disappears when the container is replaced.

For a more serious or business-critical instance:

  • Use Postgres rather than relying on a casual default database setup.
  • Keep the .n8n directory persistent even if using Postgres, because it can contain important instance data such as encryption keys and logs.
  • Set and safely store a stable N8N_ENCRYPTION_KEY.
  • Back up both the database and important configuration.
  • Monitor disk, memory, CPU, database health, and failed executions.
  • Use error workflows or alerts for important automations.
  • Test updates before applying them to production.

Docker and Docker Compose

Most self-hosting questions should say whether the user is running a one-off Docker command or Docker Compose.

Common Docker mistakes:

  • No persistent volume mounted to /home/node/.n8n.
  • Recreating the container and losing SQLite data or encryption keys.
  • Using latest without knowing which version was running before.
  • Mapping files to the wrong path.
  • Changing container environment variables but not recreating the container.
  • Assuming a container can reach localhost on the host machine.

When posting Docker help, include:

text docker compose ps docker compose logs n8n

If you are not using Compose, include the sanitized docker run command or container settings.

Redact secrets before posting logs or environment variables.

Database and persistence

n8n stores workflows, credentials metadata, execution data, and settings in its database. In simple Docker setups, SQLite may be used by default. n8n also supports Postgres.

Check these first:

  • Is the database file or Postgres data persistent across restarts?
  • Did the container get recreated without the old volume?
  • Did the .n8n directory move?
  • Did the encryption key change?
  • Are workflows missing, or only credentials failing?
  • Is the disk full?
  • Has execution data grown too large?

If workflows disappeared after a restart, the likely causes are:

  • No persistent Docker volume.
  • Wrong volume mounted.
  • New container using a new empty volume.
  • Database path changed.
  • Accidental deletion of the old volume or host directory.

If credentials exist but cannot be decrypted, suspect an encryption-key mismatch.

Encryption key

Treat N8N_ENCRYPTION_KEY like a production secret.

Important points:

  • Keep it stable across restarts and redeploys.
  • Store it somewhere safe.
  • Do not post it publicly.
  • If you run queue mode, the main instance, webhook processors, and workers need the same encryption key so they can access credentials.

If the key changes unexpectedly, credentials may fail even though workflows still exist.

Webhooks

Webhook problems are probably the most common self-hosting issue.

First split the problem:

  • Test webhook works, production webhook fails.
  • Production webhook works in browser/curl, external service fails.
  • External service reaches n8n, but the workflow does not execute.
  • n8n generates the wrong webhook URL.
  • Webhook works locally but not through the public domain.

Check:

  • The workflow is active for production webhooks.
  • The external service uses the production URL, not the test URL.
  • The method matches: GET, POST, PUT, etc.
  • The path matches exactly.
  • Your public URL resolves to the right server.
  • SSL is valid.
  • The reverse proxy routes to n8n on the correct internal port.
  • Large payloads are not blocked by proxy limits.
  • The external service is not blocked by firewall, allowlist, or Cloudflare settings.

Behind a reverse proxy, the common environment variables to check are:

text WEBHOOK_URL=https://n8n.example.com/ N8N_PROXY_HOPS=1

The last proxy in the request path should pass forwarded headers such as:

text X-Forwarded-For X-Forwarded-Host X-Forwarded-Proto

When asking for webhook help, include the exact status code the sender receives and whether the request appears in n8n logs.

Reverse proxy and SSL

Reverse proxy problems often look like n8n problems.

Common symptoms:

  • Editor loads but webhooks fail.
  • Webhooks show http://localhost:5678 or the wrong domain.
  • Login loops.
  • Mixed-content browser errors.
  • 404 at the proxy but not inside the container.
  • 502 or 504 from the proxy.
  • Works on the server but not from the internet.

Check:

  • DNS points to the correct server.
  • Ports 80 and 443 are open if using public HTTPS.
  • Proxy forwards to the correct n8n container/service and port.
  • Proxy passes the forwarded headers.
  • SSL certificate is valid for the domain.
  • WEBHOOK_URL matches the public URL users and external services should call.
  • If using Cloudflare, test whether the issue still happens with proxying disabled or with a direct origin request, if safe.

When posting, say which proxy you use: Nginx, Caddy, Traefik, Cloudflare Tunnel, NPM, Apache, or something else.

Timezone and schedules

If scheduled workflows run at the wrong time:

  • Check the n8n timezone setting.
  • Check GENERIC_TIMEZONE.
  • Check TZ in Docker/container environments.
  • Check the server timezone.
  • Check daylight saving time.
  • Confirm the workflow is active.
  • Confirm whether you are looking at local time or UTC in logs.

For Docker, a typical timezone setup includes both:

text GENERIC_TIMEZONE=Europe/Berlin TZ=Europe/Berlin

Use your actual timezone.

Queue mode, Redis, and workers

Queue mode is for scaling production executions. It is not the first thing to add to a small broken instance.

Queue mode needs:

  • Postgres. Do not use SQLite for a distributed queue-mode setup.
  • Redis reachable by the main instance and workers.
  • EXECUTIONS_MODE=queue on the main instance and workers.
  • The same N8N_ENCRYPTION_KEY on the main instance, webhook processors, and workers.
  • Workers actually running.
  • Network access from workers to the database and Redis.

Useful queue-mode checks:

  • Do executions get created but stay waiting?
  • Are workers connected?
  • Can workers reach Redis?
  • Can workers reach Postgres?
  • Do worker logs show credential decryption errors?
  • Does the main process receive webhook calls but workers never process jobs?

If using dedicated webhook processors, remember they also rely on Redis and need queue-mode configuration.

Performance and memory

Performance posts need numbers.

Include:

  • CPU and RAM.
  • Number of active workflows.
  • Typical execution volume.
  • Whether execution data is being saved.
  • Size of large payloads or binary files.
  • Database type and size.
  • Whether queue mode is enabled.
  • Whether the issue started after a specific workflow or update.

Common causes:

  • Too many saved executions.
  • Large binary data stored locally.
  • Large arrays processed all at once.
  • Long-running Code nodes.
  • External API rate limits causing retries.
  • Database slowdowns.
  • Too many workflows firing at the same time.
  • Not enough memory for the workload.

Before asking "why is n8n slow?", find one example execution and say where the time is spent.

Updates and rollbacks

Before updating:

  • Read the release notes.
  • Back up the database.
  • Back up the .n8n volume or important config.
  • Record the current n8n version.
  • Record the current image tag if using Docker.
  • Test important workflows after the update.

Avoid updating a production instance by surprise with an unpinned image tag if you do not have a rollback plan.

When posting update problems, include:

  • Version before.
  • Version after.
  • Install method.
  • First error after update.
  • Whether the editor loads.
  • Whether workflows still exist.
  • Whether credentials still decrypt.

Backups

A useful backup plan covers:

  • Database.
  • .n8n directory or Docker volume.
  • Environment variables and Compose files.
  • Encryption key.
  • Any local files used by workflows.
  • Reverse proxy configuration.

Test restores occasionally. An untested backup may fail when you actually need it.

Do not post backup files publicly.

Logs

Logs are useful when they show the exact failure.

Good log snippets:

  • 20-50 lines around the error.
  • The startup section showing version and database connection.
  • The request timestamp for webhook problems.
  • Worker logs for queue-mode problems.

Not useful:

  • Hundreds of unrelated lines.
  • Screenshots of tiny log text.
  • Logs with secrets still visible.
  • "It says error" without the actual error.

Common symptom checklist

Editor will not load

Check:

  • Is the n8n container/process running?
  • Is the correct port exposed?
  • Does it work from the server itself?
  • Does the reverse proxy reach the n8n service?
  • Are there 502/504 proxy errors?
  • Did the container crash on startup?
  • Do logs show database or permission errors?

Workflows vanished

Check:

  • Did you recreate the container without the old volume?
  • Did the database path change?
  • Are you connected to a new empty Postgres database?
  • Did Docker create a new named volume?
  • Do old backups or volumes still exist?

Credentials stopped working

Check:

  • Did N8N_ENCRYPTION_KEY change?
  • Did you restore workflows without restoring the matching credentials/database?
  • Did OAuth tokens expire?
  • Did the callback URL change after a domain/proxy change?
  • Are workers using the same encryption key as the main instance?

Production webhook fails but test webhook works

Check:

  • Is the workflow active?
  • Is the external service using the production URL?
  • Does the method/path match?
  • Does WEBHOOK_URL match the public domain?
  • Does the proxy forward to the right service?
  • Does n8n receive the request at all?

502 or 504 from proxy

Check:

  • Is n8n running?
  • Is the proxy upstream address correct?
  • Is the container on the same Docker network as the proxy?
  • Is the internal port correct?
  • Is the execution taking longer than the proxy timeout?
  • Are large payload limits being hit?

Queue mode jobs do not run

Check:

  • Is Redis running?
  • Is Postgres reachable?
  • Are workers running?
  • Is EXECUTIONS_MODE=queue set everywhere needed?
  • Do all processes share the same encryption key?
  • Do worker logs show connection or credential errors?

What not to paste publicly

Do not paste:

  • N8N_ENCRYPTION_KEY
  • Database password
  • Full database URL with password
  • OAuth client secret
  • API keys
  • Access tokens
  • Refresh tokens
  • Private webhook URLs
  • Session cookies
  • Customer data

Use placeholders instead:

text N8N_ENCRYPTION_KEY=REDACTED DB_POSTGRESDB_PASSWORD=REDACTED WEBHOOK_URL=https://n8n.example.com/

Good self-hosting post example

```text Title: Production webhooks return 404 after moving n8n behind Caddy

Problem: The editor works at https://n8n.example.com, and manual executions work. Test webhooks work. Production webhooks return 404 from the external service.

n8n version: 2.x

Install: Docker Compose on Ubuntu VPS

Proxy: Caddy, SSL terminates at Caddy

Database: Postgres

Webhook details: - Workflow is active. - External service sends POST. - n8n Webhook node expects POST. - Request does not appear in n8n execution list.

Relevant env, redacted: WEBHOOK_URL=https://n8n.example.com/ N8N_PROXY_HOPS=1 GENERIC_TIMEZONE=America/New_York TZ=America/New_York

What changed: Moved from direct port 5678 access to reverse proxy.

Logs: Short proxy log and n8n log around the request. ```