n8n Troubleshooting Guide: Fix Webhooks, Execution Timeouts, and Reverse Proxy Errors
If you’ve ever tried to troubleshoot n8n errors after a fresh install, you already know the difficulties. Everything works fine during setup, then things go wrong after you push to production. This guide covers the most common n8n errors, including webhook URLs not firing, 502 and 504 errors behind Nginx or Caddy, workflows timing out, and environment variables that silently disappear after a Docker Compose migration.
You need n8n running in Docker on a Linux VPS, a domain with DNS pointed to your server, and either Nginx or Caddy as a reverse proxy. If you haven’t set it up, you can check this guide on Installing n8n on a VPS.
Table of Contents
1. n8n Webhook URLs Not Firing
This is the most common issue for self-hosted n8n. The symptom is that your webhook works perfectly in test mode when you click Listen for test event, but in production, it returns a 404 or never triggers.
When n8n runs behind a reverse proxy, it builds the webhook URL internally by combining:
N8N_PROTOCOL, N8N_HOST, and N8N_PORT
Since n8n listens on port 5678 internally, the generated URL looks like:
http://your-server:5678/webhook/…
This is wrong for external callers who reach your server on port 443. The fix is to override the webhook URL manually.
Fix 1: Set WEBHOOK_URL in Docker Compose
To troubleshoot n8n errors for Webhook, open your docker-compose.yml file:
nano ~/n8n/docker-compose.yml
Add or update the environment section:
services:
n8n:
image: docker.n8n.io/n8nio/n8n
environment:
- WEBHOOK_URL=https://your-domain.com/
- N8N_PROXY_HOPS=1
- N8N_HOST=your-domain.com
- N8N_PROTOCOL=https
- N8N_PORT=5678
Apply the changes:
docker compose down && docker compose up -d
Expected output:
[+] Running 1/1
✔ Container n8n Started
Fix 2: Verify the Webhook URL Is Correct Inside the Container
After restarting, you must check that the environment variables are loaded correctly:
docker exec -it n8n printenv | grep -i webhook
Expected output:
WEBHOOK_URL=https://your-domain.com/
If you see localhost:5678 here, the variable did not load. Check for typos in your docker-compose.yml or ensure your .env file is referenced properly.
Fix 3: Test the Production Webhook with curl
N8n has two webhook URLs, one for testing and one for production. Make sure you’re using the Production URL without the word test in it:
curl -X POST https://your-domain.com/webhook/your-path \
-H "Content-Type: application/json" \
-d '{"test": "hello"}'
Expected output:
{"status": "success"}
If you get a 404 error, double-check that:
- The workflow is active, not just saved.
- You are using the production URL, not the test URL.
- The workflow has been saved at least once. Unsaved workflows do not register webhooks.
Fix 4: Set N8N_PROXY_HOPS for Proxy Header Trust
When n8n sits behind a reverse proxy, it needs to know how many proxy layers are in front of it so it can trust the X-Forwarded-* headers:
environment:
- N8N_PROXY_HOPS=1
Also, this is documented in the official n8n docs as a required step when using any reverse proxy.
2. How to Troubleshoot n8n Errors Behind Nginx (502 / 504)
502 Bad Gateway and 504 Gateway Timeout are the most common errors when you troubleshoot n8n errors in production. They almost always come from one of three causes:
- Nginx’s default read timeout is too low.
- Buffer limits on large webhook payloads.
- WebSocket support is missing for the n8n editor.
Check if your error happens at a consistent time, for example, always around 60 seconds. That is a clear sign of a proxy timeout, not an application bug:
# Watch n8n logs in real time
docker logs -f n8n
# Check Nginx error logs
sudo tail -f /var/log/nginx/error.log
Full Working Nginx Config for n8n
Create or replace your Nginx site config:
sudo nano /etc/nginx/sites-available/n8n
Paste the following complete configuration:
# WebSocket upgrade mapping
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 443 ssl http2;
server_name your-domain.com;
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
location / {
proxy_pass http://localhost:5678/;
proxy_http_version 1.1;
# Headers
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
# WebSocket support
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# Timeouts — increase for long-running workflows
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_connect_timeout 300s;
# Disable buffering (important for n8n streaming responses)
proxy_buffering off;
proxy_cache off;
chunked_transfer_encoding off;
# Allow large webhook payloads
client_max_body_size 50M;
}
}
server {
listen 80;
server_name your-domain.com;
return 301 https://$host$request_uri;
}
Test and reload Nginx:
sudo nginx -t
sudo systemctl reload nginx
Key Settings:
| Setting | Default | Recommended | Why |
|---|---|---|---|
| proxy_read_timeout | 60s | 3600s | Long workflows exceed 60s easily |
| proxy_send_timeout | 60s | 3600s | Prevents drops on large responses |
| client_max_body_size | 1M | 50M | Large JSON payloads from webhooks |
| proxy_buffering | on | off | Required for n8n’s streaming output |
| proxy_http_version | 1.0 | 1.1 | Required for WebSocket and keepalive |
If n8n is in the Same Docker Network as Nginx
Do not use localhost as the proxy target if both containers are on the same Docker network. Use the container name instead:
proxy_pass http://n8n:5678/;
And make sure both services share the same Docker network in docker-compose.yml:
services:
nginx:
networks:
- n8n_net
n8n:
networks:
- n8n_net
networks:
n8n_net:
3. Fix Caddy Reverse Proxy Errors in n8n
Caddy is simpler to configure than Nginx, but it still has some troubles when used with n8n. The most common issue is n8n generating webhook URLs with :5678 appended because Caddy is not forwarding the correct port headers.
Working Caddyfile for n8n
To troubleshoot n8n errors for the Caddy reverse proxy, adjust the file:
sudo nano /etc/caddy/Caddyfile
your-domain.com {
reverse_proxy localhost:5678 {
header_up X-Forwarded-Proto https
header_up X-Forwarded-Port 443
header_up Host {host}
flush_interval -1
}
}
The flush_interval -1 line disables response buffering, which is important for n8n’s real-time log streaming.
Reload Caddy:
sudo systemctl reload caddy
Test that the headers are passing correctly:
curl -I https://your-domain.com/webhook/test
Look for X-Forwarded-Proto: https in the response headers. If it’s missing, the proxy headers are not being forwarded.
If n8n is in a Docker Container and Caddy is on the Host
your-domain.com {
reverse_proxy localhost:5678 {
header_up X-Forwarded-Proto https
header_up X-Forwarded-Port 443
}
}
If both are in Docker Compose, use the container name:
your-domain.com {
reverse_proxy n8n:5678 {
header_up X-Forwarded-Proto https
header_up X-Forwarded-Port 443
}
}
4. Fix Execution Timeouts on Long Workflows in n8n
When you troubleshoot n8n errors related to long workflows stopping mid-run, the culprit is usually one of two things:
- The n8n execution timeout environment variable.
- The reverse proxy is cutting the connection before n8n finishes.
N8n uses two separate environment variables for timeouts:
| Variable | Default | Description |
|---|---|---|
| EXECUTIONS_TIMEOUT | -1 (disabled) | Default timeout in seconds for all workflows |
| EXECUTIONS_TIMEOUT_MAX | 3600 | Maximum time in seconds a user can set per workflow |
Important Note: EXECUTIONS_TIMEOUT values are in seconds, not milliseconds. Setting EXECUTIONS_TIMEOUT=3600 means 1 hour.
Set Execution Timeout in Docker Compose
services:
n8n:
image: docker.n8n.io/n8nio/n8n
environment:
- EXECUTIONS_TIMEOUT=7200 # 2 hours default
- EXECUTIONS_TIMEOUT_MAX=14400 # 4 hours max per workflow
Apply the changes:
docker compose down && docker compose up -d
Verify the variables loaded:
docker exec -it n8n printenv | grep EXECUTIONS
Expected output:
EXECUTIONS_TIMEOUT=7200
EXECUTIONS_TIMEOUT_MAX=14400
Fix for Code Node Timeouts (Task Runner)
If you upgraded n8n to version 1.x or 2.x and your Code nodes now time out after 5 minutes, the new task runner system may be the cause.
Add this to your environment:
environment:
- N8N_RUNNERS_ENABLED=true
- N8N_RUNNERS_TASK_TIMEOUT=600000 # 10 minutes in milliseconds
If the problem persists, you can disable the new runner system to fall back to the older method:
environment:
- N8N_RUNNERS_ENABLED=false
Check What Is Actually Timing Out
Run this to see what is happening when a workflow stops:
docker logs n8n --since 30m | grep -i "timeout|error|killed"
If you see no timeout-related log entries from n8n but the execution still stopped, the timeout is coming from your reverse proxy; go back to Section 2 and increase proxy_read_timeout.
5. Fix n8n Broken Environment Variables After Moving to Docker Compose
When you migrate n8n from a bare Docker run command to Docker Compose, environment variables often stop working, especially if you used an .env file incorrectly.
Step 1: Check if Your .env File Is in the Right Place
Docker Compose automatically reads a .env file only if it is in the same directory as your docker-compose.yml.
ls -la ~/n8n/
You should see:
docker-compose.yml
.env
If the .env file is missing, create it:
cp .env.example .env
nano .env
Step 2: Verify Docker Compose Picks Up the Variables
docker compose config
This command prints the fully resolved configuration. Search for your variable in the output:
docker compose config | grep WEBHOOK_URL
Expected output:
WEBHOOK_URL: https://your-domain.com/
If the variable is blank or missing, the .env file is not being loaded. Check for syntax errors, no spaces around =, no quotes unless necessary:
# Correct
WEBHOOK_URL=https://your-domain.com/
# Wrong: spaces break variable loading
WEBHOOK_URL = https://your-domain.com/
Step 3: Reference Variables Correctly in docker-compose.yml
There are two ways to pass variables. Use one, not both:
Method A. inline value, no .env file needed:
environment:
- WEBHOOK_URL=https://your-domain.com/
- N8N_PROXY_HOPS=1
Method B. reference from .env file:
environment:
- WEBHOOK_URL=${WEBHOOK_URL}
- N8N_PROXY_HOPS=${N8N_PROXY_HOPS}
Your .env file:
WEBHOOK_URL=https://your-domain.com/
N8N_PROXY_HOPS=1
Step 4: Force a Clean Restart
Docker Compose may cache old values. Do a full restart to make sure fresh variables are applied:
docker compose down
docker compose up -d
Verify all key variables inside the running container:
docker exec -it n8n printenv | grep -E "WEBHOOK|N8N_HOST|N8N_PROTOCOL|EXECUTIONS"
Expected output:
WEBHOOK_URL=https://your-domain.com/
N8N_HOST=your-domain.com
N8N_PROTOCOL=https
EXECUTIONS_TIMEOUT=7200
Useful n8n Debug Commands Cheat Sheet
When you need to troubleshoot n8n errors quickly without reading through logs manually, these commands save time:
# View all n8n container logs (last 100 lines)
docker logs n8n --tail 100
# Follow logs in real time
docker logs -f n8n
# Check all environment variables inside container
docker exec -it n8n printenv
# Test a webhook endpoint directly
curl -v -X POST https://your-domain.com/webhook/your-path \
-H "Content-Type: application/json" \
-d '{"key":"value"}'
# Check if port 5678 is reachable locally (bypassing reverse proxy)
curl -v http://localhost:5678/healthz
# Check Nginx error log
sudo tail -50 /var/log/nginx/error.log
# Test Nginx config before reloading
sudo nginx -t
# Reload Nginx without downtime
sudo systemctl reload nginx
# Reload Caddy
sudo systemctl reload caddy
# Check Docker network to confirm containers can see each other
docker network inspect bridge
# Inspect which networks a container is on
docker inspect n8n | grep -A 20 Networks
Running Complex or Scaled Workflows
If your n8n instance handles high-volume automations or long-running jobs, a single-container setup may not be enough. Queue mode lets you separate the main process from workers, so one slow workflow does not block others.
To troubleshoot n8n errors related to performance and scaling at the same time, read the n8n Queue Mode scaling guide, which covers Redis integration, worker configuration, and how to set EXECUTIONS_MODE to queue.
A reliable Linux VPS with consistent uptime is also important. If your server runs out of memory or restarts unexpectedly, all active executions are lost. For stable n8n hosting with enough RAM for Docker and a database, take a look at PerLod Linux VPS plans; they’re a solid base for production automation setups.
Conclusion
Most n8n production issues depend on a few root causes, such as wrong WEBHOOK_URL, Nginx/Caddy timeouts that are too short, execution timeout variables set incorrectly, and .env files not loading properly. When you troubleshoot n8n errors, always start by checking your environment variables inside the container, then verify your reverse proxy logs, and finally test your webhook URL directly with curl.
For domain setup and SSL, a proper domain pointing to your VPS IP is required for HTTPS webhooks. You can easily register and manage domains at PerLod Domain Registration.
FAQs
What does N8N_PROXY_HOPS=1 do?
It tells n8n to trust the X-Forwarded-* headers from one proxy layer in front of it. Without this, n8n may read the wrong IP or protocol and generate incorrect webhook URLs.
Is Caddy or Nginx better for n8n?
Both work well. Caddy is simpler and handles SSL automatically. Nginx gives you more control over timeouts and buffers. For most self-hosted setups, Caddy is faster to configure correctly.
Why does my n8n workflow stop after exactly 60 seconds?
That is Nginx’s default proxy_read_timeout. Set it to 3600s or higher in your Nginx config.