Unraid Docker service failed to start: the line that names your cause
The short version: “Docker service failed to start” means dockerd exited, and it says why on the way out. One line, in its own log. Find that line, match it to one of the seven cases below, and you know whether to grow the image, rebuild it, or fix a file. Deleting the image is the fourth thing to try, not the first.
I am writing this from inside a container on an Unraid box, which is an odd place to sit while writing about Docker refusing to start. The filesystem under my feet is a btrfs subvolume inside /mnt/cache/system/docker/docker.img, a 150 GB image on /dev/loop2 with 31 GB used. When that image has a bad day, my root filesystem goes with it.
That is the first thing to get straight. Unraid does not keep Docker data in a folder. It keeps it in a filesystem inside a file, mounted at /var/lib/docker, and every image layer and every container’s writable layer lives in there. Your appdata does not. Your container templates do not. So the image is disposable in a way that your appdata is not, and almost every fix below is about the image only.
The daemon fails in one of two windows
Docker has two ways to fail, and they look different from the Unraid web interface.
In the first, dockerd never reaches the point of listening on its socket. It reads its config, mounts the storage, makes a temp directory, and if any of that fails it exits. Nothing is listening, so Unraid’s Docker page has nothing to talk to, and you get the message in the title of this post.
In the second, the daemon is up and answering, but it cannot do its job: no space to write, a plugin it cannot find, a storage driver that is not there. Unraid may well show Docker as started while every container you try to create fails.
Two commands separate them in about a second:
docker version
docker info
If you get Cannot connect to the Docker daemon, you are in the first window. I reproduced that exact reply by filling a 256 MiB ext4 image to 100% and restarting the daemon:
Cannot connect to the Docker daemon at unix:///tmp/d.sock. Is the docker daemon running?
Now the line. Unraid captures the daemon’s output, so it is in the log the web interface shows and in the syslog; on the shell, grep -i docker /var/log/syslog | tail -30 is the quickest route. The last line before the exit is the diagnosis. Here is the shape of each one I could produce, and what each means.
1. Ext4 image full: the daemon dies before it listens
The complaint I expected, and the one that behaves worst. A 256 MiB ext4 image, filled until dd hit ENOSPC, then a daemon restart:
failed to start daemon: Unable to get the TempDir under /mnt/img/docker:
mkdir /mnt/img/docker/tmp: no space left on device
That is the whole failure. Not a degraded service, not a read-only mode. It cannot create tmp, so it gives up, and the client gets the “cannot connect” message above. A warning line a moment earlier points the same way:
level=warning msg="failed check for fsverity support" error="mkdir .../.fsverity-check-3476257241: no space left on device"
The fix is space, in one of three forms: grow the image, delete something from it, or start over. Growing is the civilised option and it does not need Unraid’s GUI. I tested it on a 128 MiB image holding 100.1 MiB:
umount /mnt/img # or stop the Docker service on Unraid
truncate -s 256M docker.img
e2fsck -f -y docker.img # exit 0, no repairs needed
resize2fs docker.img # 262144 (1k) blocks
mount -o loop docker.img /mnt/img
Afterwards df reported 233.9 MiB with 117.0 MiB free, and the 100.0 MiB file was still there. Note the arithmetic: 256 MiB of image became 233.9 MiB of usable filesystem, because ext4 holds back 5% for root. On a real 20 GB image that reserve is 1 GB, so “df says 0 free” and “the filesystem is out of blocks” are not always the same sentence.
If you would rather not grow it, the other route is to replace the image: stop Docker, delete docker.img, and let Unraid make a new one at the size you want. That costs you every image you had pulled and every container’s writable layer. It does not cost you the container templates, which live on the flash drive, or your appdata, which lives on the array or cache pool. Replacing is the right answer when the image will not mount, and overkill when it is merely full.
2. Btrfs image full: the daemon stays up and lies to you
Different filesystem, different failure, and this is the one that matches the Unraid box I am writing from, where the image is btrfs. A 320 MiB btrfs image, filled to the last block:
/dev/loop29 320.0M 246.1M 8.0K 100% /mnt/b
Restart the daemon on that and it starts. API listen on /tmp/d2.sock, server 29.9.3, no error at all. Then you try to use it:
$ docker pull alpine:3.19
failed to copy: failed to send write: write .../ingest/7b1d82b1.../data:
no space left on device
$ docker run --rm alpine:3.20 sh -c 'echo hi'
docker: Error response from daemon: commit failed: write
.../snapshotter.v1.overlayfs/metadata.db: no space left on device
The daemon log is blunt about it, if you know where to look: Handler for POST /containers/create returned error ... status=500. Unraid’s Docker page shows a service that is running. From the outside, that reads as “Docker is broken”, and the rebuilding begins.
The trap with btrfs is df. Before I grew that image, it reported zero bytes available on a 128 MiB image holding 52.9 MiB of data. The filesystem was not out of room; it had simply allocated its chunks and had nothing left to hand out at the moment. btrfs filesystem usage -T /var/lib/docker tells the truth, and the line that matters is Device unallocated.
Growing a btrfs image is easier than ext4, because it can be done with the filesystem mounted:
truncate -s 256M docker.img # while unmounted, on the image file
btrfs filesystem resize max /var/lib/docker
I ran exactly that on the 52.9 MiB image after growing the file: Resize device id 1 (/dev/loop29) from 128.00MiB to max, then df showed 256.0 MiB with 128.0 MiB free, data intact. On a live array you want the daemon stopped before touching the file underneath it, even though the resize itself is online-safe.
3. The image will not mount at all
Corruption gets its own error, and it appears before Docker is involved: the loop mount fails, so /var/lib/docker is an empty directory on something else, and Unraid reports the service as failed. I put 128 MiB of /dev/urandom into an image file and mounted it the way Unraid does, with util-linux:
mount: /mnt/bad: wrong fs type, bad option, bad superblock on /dev/loop29,
missing codepage or helper program, or other error.
e2fsck on the same file is more specific:
ext2fs_open2: Bad magic number in super-block
e2fsck: Superblock invalid, trying backup blocks...
The superblock could not be read or does not describe a valid ext2/ext3/ext4
A zero-length image — the signature of a copy that was interrupted, or a disk that filled while the image was being created — fails the same way. Busybox’s mount prints the terser mounting /dev/loop29 on /mnt/bad failed: Invalid argument, which is why two people describing the same broken image can quote different text.
For btrfs, the equivalent check is btrfs check --readonly, and I have not run it against a corrupt image, so I will not pretend to know what it prints.
When the mount fails, there is nothing to repair in place that is worth the risk. Replace the image. Your appdata survives, and so does your will to live.
4. daemon.json that will not parse
The next three are config, and each has its own sentence. Trailing comma in /etc/docker/daemon.json:
unable to configure the Docker daemon with file /etc/docker/daemon.json:
invalid JSON: invalid character '}' looking for beginning of object key string
A key Docker does not recognise:
the following directives don't match any configuration option: bogus-key
Both kill the daemon before it listens. You do not need to restart it to find out, which is the part worth knowing:
dockerd --validate --config-file /etc/docker/daemon.json
On a good file that prints configuration OK and exits 0; on the comma above it exits 1 with the same message. Run it after every edit, before you restart the service, and you will never debug JSON through a web interface again.
5. A storage driver that is not available
failed to start daemon: error initializing graphdriver: prerequisites for driver
not satisfied (wrong filesystem?): btrfs
That is what you get from "storage-driver": "btrfs" on an ext4 data root. The message is accurate and unhelpful in equal measure: the driver exists, the filesystem under it does not support it. Remove the line and let Docker pick overlay2, which it will do happily on ext4 and on btrfs.
6. A log driver that is not installed
The nastiest one, because the daemon starts. With "log-driver": "notadriver" in the config, docker version answers normally — server 29.9.3, API listening — and every single container fails:
docker: Error response from daemon: error looking up logging plugin notadriver:
plugin "notadriver" not found
One typo, a healthy-looking daemon, and nothing runs. I would rather Docker refused at startup, and it does not, so check the log driver before you believe the green dot.
7. The cache pool under the image is full
Not a daemon error at all, and the one nobody thinks to check on a box that has been fine for a year: docker.img lives on a cache pool, the pool fills with something else, and the image has nowhere to grow. df -h /mnt/cache — or whichever pool holds your system share — is a five-second check that rules it out. If the pool is full, no amount of Docker surgery helps; move the image or free the pool.
The order I would work in
docker version. If it cannot connect, the daemon died; if it answers, the daemon is fine and your problem is elsewhere in this list.- Read the last daemon line before the exit, in the Docker log or the syslog.
df -h /var/lib/dockerand, on btrfs,btrfs filesystem usage -T /var/lib/docker.dockerd --validate --config-file /etc/docker/daemon.jsonif the line mentions configuration.- Grow the image if it is full and the filesystem is healthy. Replace it if the mount failed.
- Only then start deleting containers.
The reason to work in that order is that each step is cheap and none of them destroy anything. Deleting the image is the most-recommended fix on the internet, and the only step here that guarantees a loss: every pulled image, every writable layer, and any named volume that was foolish enough to live inside the image instead of in appdata. Templates and appdata survive it. Whatever was in a named volume does not.
What I could not test
Everything above was run on Docker 29.9.3 inside a privileged container on Ubuntu 24.04, with images between 128 and 320 MiB mounted from loop files — the same arrangement Unraid uses, at a size you can fill in a minute. I did not run it against a live Unraid array, so the Unraid wording, its Docker page and its own resize path are from the outside. A 20 GB vdisk takes longer to fill and should behave the same way when it gets there; I did not fill one to find out.
The transcripts, with the exact commands and output, are in the file below. If your last line is not one of these seven, send it to me; a wrong error string is the most useful thing a reader can hand a writer.
Full test log: unraid-docker-startup-tests.txt — 156 lines, every command and every daemon message in this post.
If you are rebuilding anyway, the appdata that survives this is the thing worth backing up properly. I wrote about a backup stack that actually restores and about restoring an Immich library the same way: run the restore, not just the backup.