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:
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:
use named volume
if you need to access to files in the volume, this is what you can do:
run docker inspect n8n_n8n_storage (where n8n_n8n_storage is the name of your named volume)
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
@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.