# n8n Migration: WEBHOOK_URL + Workflow Webhook Fix When migrating n8n from one domain to another, two things MUST be updated: ## 1. Environment Variable — .env ``` WEBHOOK_URL=https://NEW_DOMAIN/ ``` This controls the base URL for all webhook nodes. Restart n8n after changing. ## 2. Existing Workflow Webhook URLs — PostgreSQL n8n stores webhook URLs in the `nodes` JSON column of `workflow_entity`. After changing `WEBHOOK_URL`, existing workflows still have old URLs baked in. Update them directly: ```sql UPDATE workflow_entity SET nodes = regexp_replace( nodes::text, 'https://old\.domain\.com', 'https://new.domain.com', 'g' )::json; ``` Then restart n8n to reload workflows from the database. ## Full Migration Steps 1. `docker compose down` on old server 2. `tar czf n8n-data.tar.gz` Postgres volume + n8n data volume 3. `scp` to new server 4. Update `.env`: `N8N_HOST`, `N8N_PROTOCOL`, `WEBHOOK_URL` to new domain 5. `docker compose up -d` on new server 6. Fix volume permissions: `chown -R 1000:1000` on n8n data volume (node user inside container = UID 1000) 7. Update workflow webhook URLs via SQL above 8. Restart n8n: `docker compose restart n8n` 9. Verify: `SELECT nodes::text LIKE '%old.domain.com%' FROM workflow_entity WHERE name LIKE '%workflow_name%';` → should return `f` ## Common Pitfall Sub-path routing (e.g., `app1.domain.com/n8n/`) breaks n8n because its SPA uses absolute asset paths (`/assets/`, `/static/`). Always use a dedicated subdomain (`n8n.domain.com`).