service "web" refers to undefined volume appdata: invalid compose project — a service mounts a named volume, appdata:/data, and the Compose file has no top-level volumes: section that declares appdata. Add the declaration and the project starts.

The fix is three lines at the end of the file: volumes:, the volume's name with a colon, and optionally a name: for the volume on the engine. Then run docker compose up -d again.

The error

PS> docker compose up -d
service "web" refers to undefined volume appdata: invalid compose project

The command exited with code 1. docker compose config printed the same line and exited with code 1 as well, so you can check a file without starting anything. The file that produced it:

name: fix8-d3-vol
services:
  web:
    image: alpine
    command: ["sleep", "5"]
    volumes:
      - appdata:/data

Why it happens

In a service's volumes list, the part before the colon can be a path such as ./appdata, a bind mount of a host folder, or a plain word such as appdata, the key of a named volume. A named volume's key has to appear under the top-level volumes: section of the same file. Here it does not, and Compose rejected the project before it created a network, a volume or a container.

You meet this after copying one service out of a longer file, where the declaration stayed behind, or after a typo between the service's line and the declaration. The message names both the service and the missing key, so search the file for that key.

The fix

name: fix8-d3-vol
services:
  web:
    image: alpine
    command: ["sleep", "5"]
    volumes:
      - appdata:/data
volumes:
  appdata:
    name: fix8-d3-appdata
PS> docker compose up -d
 Volume fix8-d3-appdata Creating
...
 Volume fix8-d3-appdata Created
...
 Container fix8-d3-vol-web-1 Started
PS> docker compose ps --format '{{.Name}} {{.State}}'
fix8-d3-vol-web-1 running

The name: line is optional; it sets the volume's name on the engine. An empty declaration, appdata: on its own line, also cleared the error; Compose then created the volume as fix8-d3-vol2_appdata, the project name plus the key. In these tests docker compose down -v removed the declared volume along with the container and the default network, and plain docker compose down left the volume in docker volume ls, which is what you want for data.

If you meant a host folder rather than a volume, write it as a path instead, for example ./appdata:/data; with that line and no top-level declaration, docker compose config --quiet exited with code 0.

How it was reproduced

One alpine service running sleep 5 with appdata:/data in its volumes list and no top-level volumes: section, started with docker compose up -d from Windows PowerShell 5.1 on Docker Engine 29.9.2 and Docker Compose v5.6.1 (Docker Desktop on Windows 11). The project and volume names carry this test's fix8-d3- prefix. The first project was removed with docker compose down -v; the empty-declaration test was removed with docker compose down and docker volume rm fix8-d3-vol2_appdata.

Frequently asked

What does 'refers to undefined volume' mean in Docker Compose?
A service mounts a named volume, such as appdata:/data, and the file has no top-level volumes section that declares that name. Compose rejects the project and starts nothing.
How do I declare a named volume in docker-compose.yml?
Add a top-level volumes: section at the end of the file with the volume's name followed by a colon, for example appdata:. A name: line under it is optional and sets the volume's name on the engine.
Can I check a Compose file for this error without starting it?
Yes. docker compose config printed the same 'refers to undefined volume' line and exited with code 1 in this test, without creating any container, network or volume.

More decoded errors in the Fixes category. When a volume will not go away later, see Docker: volume is in use.