Use local directory instead of docker volume for n8n_data

Describe the problem/error/question

I would like to use a local folder instead of a docker volume for the /home/node/.n8n folder, but when I do it gives an error.
This is the docker compose file:

services:
  n8n:
    container_name: n8n
    image: docker.n8n.io/n8nio/n8n
    restart: always
    ports:
      - 5678:5678
    environment:
      - N8N_HOST=n8n.xx.xx
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - NODE_ENV=production
      - WEBHOOK_URL=https://n8n.xx.xx/
      - GENERIC_TIMEZONE=Europe/Rome
    volumes:
      - ./n8n_data:/home/node/.n8n
      - ./local-files:/files

What is the error message (if any)?

n8n  | No encryption key found - Auto-generating and saving to: /home/node/.n8n/config
n8n  | Error: Command "start" not found

Information on your n8n setup

  • n8n version:
  • Database (default: SQLite):
  • n8n EXECUTIONS_PROCESS setting (default: own, main):
  • Running n8n via (Docker, npm, n8n cloud, desktop app): Docker
  • Operating system: Ubuntu 24.04.2

The difference between the named volumes and bind mounts is as follows (I hope I understand this correctly based on what I read in the docs).

When you use named volume

- n8n_storage:/home/node/.n8n

Docker creates this volume automatically if it doesn’t exist, and (what is importnat) initializes it with the contents of the target directory in the image (i.e. the contents of /home/node/.n8n inside the container). This means that the the app files that n8n expects are preserved, nothing is overwritten [1].

On the other hand, with the bind mount

- ./n8n_data:/home/node/.n8n

Docker does not copy anything into this directory - it simply overrides the container’s /home/node/.n8n folder with whatever is in your local folder, which causes node_module and all the good stuff to be missing, including the start script in package.json, and I assume this is what happens in your case [2].

The easiest way to go about this is:

  1. use named volume
  2. if you need to access to files in the volume, this is what you can do:
  3. run docker inspect n8n_n8n_storage (where n8n_n8n_storage is the name of your named volume)
  4. find the MountPoint field, this is where you can access the volume on the host machine.
$ docker volume list | grep n8n_storage
    local     n8n_n8n_storage

$ docker inspect n8n_n8n_storage | grep Mount
    "Mountpoint": "/var/lib/docker/volumes/n8n_n8n_storage/_data",

$ sudo ls /var/lib/docker/volumes/n8n_n8n_storage/_data
    binaryData  config  crash.journal  custom  git  nodes  ssh

Hope it helps.

Got it, would have preferred to keep everything in the same folder but the volume will have to do.
Thanks a lot! :slight_smile:

@jabbson answer helped me to solve the problem but I wanted to use local bind. So, I wrote this script to solve this issue.

This script initializes an n8n_data folder by copying default configs from a Docker volume and sets correct permissions.

#!/bin/bash

# Set paths - you need to change this as per your setup
DATA_DIR="${HOME_DIR}/n8n-automation/n8n_data"

VOLUME_NAME="temp_n8n"

# Step 1: Create and start a temporary container to populate named volume
docker run --rm \
  --name temp-n8n-init \
  -v ${VOLUME_NAME}:/from \
  docker.n8n.io/n8nio/n8n:1.103.1 true

# Step 2: Copy from named volume to bind mount
docker run --rm \
  -v ${VOLUME_NAME}:/from \
  -v ${DATA_DIR}:/to \
  alpine sh -c "cp -r /from/. /to/"

# Step 3: Fix ownership and permissions
sudo chown -R 1000:1000 "${DATA_DIR}"
sudo chmod 600 "${DATA_DIR}/config"

# Step 4: Show success
echo "n8n_data folder is ready with correct contents and permissions."

Since I had already started the container with the volume (and started using n8n), I just stopped it, did a docker inspect to find the mountpoint and cp -ar everything to my directory. For now it looks like it is working.

knocks@basilisk /o/d/n8n> docker volume inspect n8n_n8n_data
[
    {
        "CreatedAt": "2025-07-18T19:36:57+02:00",
        "Driver": "local",
        "Labels": {
            "com.docker.compose.config-hash": "38d498770863d6bc2243973ba19c368c74a2195c888479e037b71146d586c1bb",
            "com.docker.compose.project": "n8n",
            "com.docker.compose.version": "2.38.2",
            "com.docker.compose.volume": "n8n_data"
        },
        "Mountpoint": "/var/lib/docker/volumes/n8n_n8n_data/_data",
        "Name": "n8n_n8n_data",
        "Options": null,
        "Scope": "local"
    }
]

root@basilisk:/opt/docker/n8n# cp -ar /var/lib/docker/volumes/n8n_n8n_data/_data n8n_data

then I changed the volumes section in the docker compose file from:

volumes:
   - n8n_data:/home/node/.n8n
   - ./local-files:/files

to:

volumes:
    - ./n8n_data:/home/node/.n8n
    - ./local-files:/files

Of course this preserved everything I did on n8n, and I didn’t need a “support” container to copy the data.