Help with "ghost" executions in self-hosted n8n setup with multiple workers

Dear n8n Support Team,

We are reaching out for help with a “ghost execution” issue in our self-hosted n8n infrastructure. We initially used a simple three-container setup (main n8n, PostgreSQL, runner). However, as our workload increased, we decided to scale up to the following architecture:

  • Main n8n container

  • Redis queue manager

  • Two worker+runner pairs (2 workers and 2 runners total)

  • PostgreSQL database

Host information:
Ubuntu 24.04.4 LTS + Docker
Intel(R) Xeon(R) CPU E5-2690 v4
16 GB RAM

After migration, we encountered a serious problem: multiple parallel executions of the same workflow with identical execution IDs, leading to race conditions. This causes errors when working with files (e.g., trying to delete a file on FTP that has already been deleted by the “ghost”), databases, and other components. This issue matches exactly the one described in this community post:
http://community.n8n.io/t/ghost-sub-executions-duplicated-runs-while-parent-execution-shows-single-calls/190813

We tried various solutions found online, but none helped. Moreover, even when we reduced the setup to a single worker+runner pair, the problem persisted.

We suspect that while trying to configure a scalable environment and then fix the ghosting issue, we likely made multiple configuration mistakes. Therefore, we would greatly appreciate it if you could provide an example of a correct, production‑ready configuration for two worker+runner pairs, Redis, main n8n, and a database – one that is free from ghost executions.

Thank you very much for your assistance. We look forward to your recommendations.

welcome to the n8n community @Alex.86
From what you described, I’d focus first on the queue-mode configuration consistency rather than scaling size. Duplicate executions with the same execution ID usually suggest multiple processes are competing for the same jobs or an old process is still consuming the queue. I’d verify that all containers use the same Redis/database settings, only the intended services run worker mode, and no leftover containers from the previous setup are still connected. I’d also check worker concurrency settings, container restart loops, and whether all instances are running the exact same n8n version. Before scaling to two workers again, I’d test with one clean worker + Redis + main instance from scratch and confirm the issue disappears, then scale gradually.

Thanks for the quick reply! Right now we have a simplified configuration: the main container, 1 worker and redis - the problem persists.

@Alex.86
Thanks for the update.
In this case, I’d stop looking at scaling and start looking at stale queue state or duplicated trigger sources. I’d check whether old jobs are still sitting in Redis, whether the same workflow can be triggered from more than one source, and whether any old containers/processes are still registered after previous tests. At this point, the most useful next step would be to share your docker-compose.yml (with secrets removed) and the relevant environment variables, because the issue likely lives in configuration rather than worker count.

services:
  n8n-redis:
    container_name: n8n-redis
    image: redis:latest
    networks:
      - n8n
    env_file:
      - n8n-redis.env
    volumes:
      - /home/docker/n8n/redis:/data
    command: /bin/sh -c 'redis-server --requirepass "$${REDIS_PASSWORD}"'
    healthcheck:
      test: ["CMD-SHELL", "redis-cli -a \"$${REDIS_PASSWORD}\" ping | grep -q PONG"]
      interval: 15s
      timeout: 5s
      retries: 5
      start_period: 20s
    restart: always

  n8n-db:
    container_name: n8n-db
    image: postgres:18
    networks:
      - n8n
    ports:
      - 5432:5432
    env_file:
      - n8n-db.env
    volumes:
      - /home/docker/n8n/db:/var/lib/postgresql/data
    healthcheck:
      test:
        - CMD-SHELL
        - pg_isready
      interval: 10s
      start_period: 20s
    restart: always

  n8n:
    container_name: n8n
    image: docker.n8n.io/n8nio/n8n:next
    networks:
      - n8n
    ports:
      - 5678:5678
    env_file:
      - n8n.env
    volumes:
      - /home/docker/n8n:/home/node/.n8n
      - truenas:/truenas
    user: '1000'
    depends_on:
      n8n-db:
        condition: service_healthy
      n8n-redis:
        condition: service_healthy
    deploy:
      resources:
        limits:
          cpus: 7
          memory: 12GB
    healthcheck:
      test: ["CMD-SHELL", "node -e \"require('http').get('http://127.0.0.1:5678/healthz/readiness', r => process.exit(r.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))\""]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 40s
    logging:
      driver: json-file
      options:
        max-size: 50m
        max-file: 3
    restart: always

  n8n-worker:
    container_name: n8n-worker
    image: docker.n8n.io/n8nio/n8n:next
    networks:
      - n8n
    env_file:
      - n8n-worker.env
    volumes:
      - /home/docker/n8n:/home/node/.n8n
      - truenas:/truenas
    user: '1000'
    command: worker --concurrency=10
    depends_on:
      n8n-db:
        condition: service_healthy
      n8n:
        condition: service_healthy
      n8n-redis:
        condition: service_healthy
    deploy:
      resources:
        limits:
          cpus: 7
          memory: 12GB
    healthcheck:
      test: ["CMD-SHELL", "node -e \"require('http').get('http://127.0.0.1:5678/healthz/readiness', r => process.exit(r.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))\""]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 40s
    logging:
      driver: json-file
      options:
        max-size: 50m
        max-file: 3
    restart: always

  n8n-runner:
    container_name: n8n-runner
    image: <ourgit>/it/docker/n8n-runners:20260326-pydeps1
    networks:
      - n8n
    env_file:
      - n8n-runner.env
    volumes:
      - /home/docker/n8n/runners/n8n-task-runners.json:/etc/n8n-task-runners.json
      - truenas:/truenas
    depends_on:
      n8n-worker:
        condition: service_healthy
      n8n-db:
        condition: service_healthy
    healthcheck:
      test: ["CMD-SHELL", "node -e \"const s=require('net').connect({host:'127.0.0.1',port:5680},()=>{s.end();process.exit(0)});s.on('error',()=>process.exit(1));\""]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 40s
    logging:
      driver: json-file
      options:
        max-size: 50m
        max-file: 3
    restart: always

networks:
  n8n:
    name: n8n

volumes:
  truenas:
    driver_opts:
      type: nfs
      o: addr=192.168.10.75,nolock,soft,nfsvers=4
      device: :/mnt/gipsy/n8n 

Do I need to send other configuration files to help with our problem?

@Alex.86
I’d start by replacing docker.n8n.io/n8nio/n8n:next with a pinned stable version on both main and worker (for example the same exact release tag). next changes frequently and isn’t ideal while debugging duplicate executions. I’d also fully recreate the stack after changing versions so no old containers or mixed images remain connected to Redis.