Sistava

n8n Docker Container Issues and How to Fix Them

How-to — — by Mahmoud Zalt

Permission denied on startup, workflows gone after an update, credentials that will not decrypt, and the settings that break n8n in Docker.

Self-hosting n8n in Docker is genuinely straightforward until the day it is not. The container that ran for three months restarts after an update and refuses to come up, or comes up empty, or comes up fine but nothing outside can reach it.

The pattern is nearly always the same underneath. Something that was living inside the container needed to live outside it, and the container is where n8n keeps things you cannot afford to lose.

Before any of the specific fixes below, read the logs. Run docker logs against the container name, or docker compose logs if you are using compose, and read the first twenty lines after the restart rather than the last twenty. Startup failures announce themselves at the top.

Why does n8n fail with permission denied on its config file?

Because the n8n image runs as a non-root user and the folder you mounted is owned by root. The container needs to write to the n8n data directory at /home/node/.n8n, and when Docker creates that host folder for you it belongs to root, so the process inside cannot open its own config file.

n8n 2.0 made this stricter. Their breaking changes page lists N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS as enforced by default, requiring 0600 permissions on the settings file, so setups that used to start with loose permissions now stop. The documented docker run command passes this variable explicitly, which tells you it is expected to be on.

There are three fixes people report in community threads, and they are not equally good. Correcting ownership on the host directory so it matches the user inside the container is the clean one. Turning the enforcement variable off gets you running but leaves your settings file readable by more of the system than it should be. Running the container as root works and is the option you should reach for last.

Why did my workflows disappear after an update?

Because they were never outside the container. If you started n8n without mounting a volume, everything lived in the container's writable layer, and removing the container to pull a new image removed the data with it. Nothing is recoverable from an image.

n8n's documented setup creates a named volume first and mounts it at the data directory. The commands are docker volume create n8n_data, then a docker run that includes a volume flag mapping n8n_data to /home/node/.n8n, publishes port 5678, and sets the GENERIC_TIMEZONE and TZ variables to your timezone.

  1. Check whether you have a volume at all — Run docker volume ls and look for the volume you expect. If the container was started without one, there is nothing mounted and nothing persisted.
  2. Look for orphaned volumes before you conclude anything is lost — Community threads on lost n8n data often end with the old volume still present under a different name. Inspect what is there before you rebuild.
  3. Back up the volume, not just the database — The data directory holds the encryption key as well as the SQLite file. A database backup without the key restores workflows but not credentials.
  4. Update the documented way — n8n's process is to stop the container, remove it, pull the latest image, then start it again with the same command and volume. The volume is what carries you across.
  5. Start the new container and read the first log lines — Do not open the editor first. If something is wrong with permissions, the key or the database, the log says so before the interface has a chance to look normal.
  6. Log in and open one workflow with credentials attached — Credentials are the thing that breaks quietly. A workflow list that looks right is not proof that the encryption key came across.

Why do my credentials say they could not be decrypted?

Because n8n is using a different encryption key than the one that encrypted them. The message reported repeatedly in n8n's community forum says the credentials could not be decrypted and that the likely reason is that a different encryption key was used to encrypt the data.

The key lives in the n8n data directory, so it is lost exactly when the volume is lost, and n8n generates a fresh one on the next start. That is why this shows up during a recovery: you restore the database, the workflows all appear, and every credential inside them is unreadable. You can also set the key explicitly with the N8N_ENCRYPTION_KEY environment variable, which is what you do when you are moving an instance on purpose.

If you run n8n in queue mode this stops being optional. n8n's scaling documentation states that the encryption key of the main instance must be shared with all worker and webhook processor nodes, so a worker started without it cannot use any credential your workflows rely on.

The next group of problems has nothing to do with data. The container is healthy, n8n is running, and the trouble is that the address it believes it has does not match the address anyone actually uses. This is where most first-time self-hosters lose a weekend, and the fixes are all environment variables.

Why does n8n complain about a secure cookie over http?

Because n8n issues its session cookie with the secure attribute, and a browser will only send a secure cookie back over an encrypted connection. Reach the editor over plain http and the login cannot stick, which is what the widely reported message about the server being configured to use a secure cookie while you visit over an insecure URL is telling you.

There is an N8N_SECURE_COOKIE variable people set to false to get past it, and it is a reasonable thing to do on a laptop for ten minutes. It is not a fix for anything you can reach from the internet. The real answer is to put a real certificate in front of n8n, which usually means a reverse proxy terminating TLS on a domain you own.

Why are the URLs n8n shows me unreachable from outside?

Because n8n builds them from its internal host and port unless you tell it otherwise, so it advertises port 5678 while the world talks to your domain on 443. n8n's documentation says to set the webhook URL manually so it can display the right URL in the editor and register the right one with external services.

The current variable is N8N_WEBHOOK_URL. The older WEBHOOK_URL still works but is deprecated and n8n logs a warning when it sees it. n8n also states plainly that combining N8N_HOST, N8N_PORT and N8N_PROTOCOL will not do the job behind a proxy, because the internal port and the external port are different by definition.

Two supporting settings finish the job. Your proxy must forward the X-Forwarded-For, X-Forwarded-Host and X-Forwarded-Proto headers, and N8N_PROXY_HOPS should be set to the number of proxies in front of n8n. Without that second one, n8n sees the proxy as the client, which quietly breaks any IP allowlist you configured.

None of this is difficult, and all of it is yours. Volumes, certificates, forwarded headers, an encryption key you must never lose: self-hosting hands you the whole operational job along with the control, and that job does not go away once it works. If you would rather not carry it, the alternative is a hosted employee at Sistava, where the running of the thing is our problem and you only describe the work.

One more category is worth knowing about, because it breaks containers that were running perfectly the day before. Major version upgrades change defaults, and a default you were relying on without knowing it is the hardest kind to debug.

What did n8n 2.0 change that can break a working container?

Quite a lot, and n8n publishes the list. The database changes bite hardest: MySQL and MariaDB support was dropped, so those instances must move to PostgreSQL or SQLite, and the legacy SQLite driver was removed in favour of a pooling driver with write-ahead logging enabled.

Several defaults tightened for security in the same release. Task runners are enabled by default for Code node execution, the Code node is blocked from reading environment variables, authentication is now required on the OAuth callback, and the ExecuteCommand and LocalFileTrigger nodes are disabled by default. If a workflow that used to read a key from the environment suddenly cannot, that is why.

Three environment variables were removed outright, including N8N_CONFIG_FILES, and the in-memory mode for binary data is gone, leaving filesystem, database or S3 storage. The release channels were renamed too, from latest and next to stable and beta, with the old tags kept temporarily. Read the breaking changes page before a major upgrade rather than after.

What this guide will not fix

It will not recover data that was never persisted. If the container ran without a volume and has been removed, the workflows and the encryption key are gone, and no setting brings them back. That is the one failure in this list with no fix, only a lesson.

It also will not make the ongoing job disappear. Backups, certificate renewals, version upgrades, disk space and the occasional 3am restart are the standing cost of running your own automation server, and they are a fair trade only if you actually want to be the person who does them.

Worth pricing that honestly against the alternative. A small server is cheap, and your hours are not. If you would rather compare it directly, the plans at Sistava are per month with no infrastructure attached, and the maintenance line in that comparison is zero.

If you are staying with Docker, spend twenty minutes today on the one thing that makes every future problem survivable. Take a copy of the data volume, store the encryption key somewhere separate from the server, and try a restore into a scratch container before you need it to work.

FAQ

How do I fix permission denied errors when starting n8n in Docker?

The container runs as a non-root user and needs to write to the n8n data directory at /home/node/.n8n. When Docker creates that host folder it is owned by root, so the process inside cannot open its config file. Correcting ownership on the host directory to match the user inside the container is the clean fix. n8n 2.0 also enforces strict 0600 permissions on the settings file by default through N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS, so setups that previously started with looser permissions now stop.

Where does n8n store its data in Docker?

In the directory /home/node/.n8n inside the container. n8n's documented setup creates a named volume called n8n_data and mounts it there so the data survives the container. That directory holds the SQLite database when you are not using PostgreSQL, and it also holds the encryption key, instance logs and source control assets even when you are.

Why do my n8n credentials stop working after a restore?

Because the encryption key did not come with the backup. The key lives in the n8n data directory, and if that volume is lost n8n generates a new one on the next start, leaving previously encrypted credentials unreadable. Community reports of this show a message stating that credentials could not be decrypted and that a different encryption key was likely used. You can set the key explicitly with the N8N_ENCRYPTION_KEY environment variable when moving an instance deliberately.

How do I update n8n in Docker without losing anything?

n8n's documented process is to stop the container, remove it, pull the latest image, then start it again with the same command and the same volume. The volume is what carries your data across, so confirm it exists and is mounted before you remove anything. Take a copy of the volume first, and read the breaking changes page for the version you are moving to.

Should I set N8N_SECURE_COOKIE to false?

Only for local work on your own machine. n8n issues its session cookie with the secure attribute, and browsers will not send a secure cookie back over plain http, which produces the widely reported secure cookie message when you visit over an insecure URL. Turning the setting off gets you past it, but on anything reachable from the internet the right fix is a real certificate, usually through a reverse proxy terminating TLS on a domain you control.

Why does n8n show URLs with port 5678 that nobody outside can reach?

Because n8n builds those URLs from its internal host and port unless you tell it the public address. Set N8N_WEBHOOK_URL to your public base URL so n8n displays and registers the correct address. The older WEBHOOK_URL variable is deprecated and logs a warning. n8n also documents that combining N8N_HOST, N8N_PORT and N8N_PROTOCOL will not work behind a reverse proxy, and that you should set N8N_PROXY_HOPS to the number of proxies in front of the instance.

Sources