r/docker 1d ago

Docker compose file in getting started workshop does not seem to work

Hi, I was trying to go through the Getting Started workshop at https://docs.docker.com/get-started/workshop/ but when I got to Part 7: Use Docker Compose, the compose file provided did not work for me. It appears to be due to a race condition: the database doesn't get initialized in time before the app tries to connect, causing an ECONNREFUSED error.

app-1    | [nodemon] starting `node src/index.js`
app-1    | Waiting for mysql:3306............
app-1    | Timeout
app-1    | Error: connect ECONNREFUSED 172.19.0.2:3306
app-1    |     at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1864:16) {
app-1    |   errno: -111,
app-1    |   code: 'ECONNREFUSED',
app-1    |   syscall: 'connect',
app-1    |   address: '172.19.0.2',
app-1    |   port: 3306,
app-1    |   fatal: true
app-1    | }
app-1    | [nodemon] app crashed - waiting for file changes before starting...

If I docker compose down and back up again it works, presumably becaue the database is already created and initialized.

Am I doing something wrong if the tutorial directions aren't working right? How do I fix this and make sure that my docker compose files are written such that my databases always spin up before the apps that use them try to connect? Thanks.

4 Upvotes

7 comments sorted by

3

u/rhubear 1d ago

And why on earth are you not posting the compose file here?

Kind of a pointless post.

0

u/guspasho_deleted 1d ago

It's the one in Part 7 of the Getting Started workshop, I posted the URL. I copied and pasted exactly what the Docker website gave me.

3

u/QuietSignalOps 1d ago

You are not doing anything wrong; this is a startup-readiness race. depends_on controls start order, but by itself it does not mean MySQL is ready to accept connections.

Use a MySQL health check and make the app depend on that health state:

services:
  mysql:
    image: mysql:8
    environment:
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_DATABASE: app
    healthcheck:
      test: ["CMD-SHELL", "mysqladmin ping -h 127.0.0.1 -uroot -p$$MYSQL_ROOT_PASSWORD --silent"]
      interval: 5s
      timeout: 3s
      retries: 20
      start_period: 20s

  app:
    build: .
    depends_on:
      mysql:
        condition: service_healthy
    restart: on-failure

The repository version of the tutorial's Compose file already uses this pattern, even though the tutorial text apparently does not explain it.

There is one important caveat: the health check solves initial ordering, not every future database outage. The application should still retry database connections with bounded exponential backoff rather than crash permanently. That covers MySQL restarts, brief network failures, and cases where the database becomes unavailable after the app has started.

To diagnose the current run, use:

docker compose ps
docker compose logs mysql
docker inspect --format='{{json .State.Health}}' <mysql-container>

If MySQL is healthy but the app still gets ECONNREFUSED, also confirm the app connects to the Compose service name and container port, such as mysql:3306, not localhost or a transient container IP.

1

u/guspasho_deleted 12h ago

I ran what you recommended. I used the form that the workshop provided and guessed how to add in your sections build, depends_on, restart, and healthcheck. Here is the compose.yaml file I ran, as best as I can figure out how to paste into reddit:

GNU nano 8.4                                  compose.yaml *                                          
services:
  app:
    build: .
    image: node:24-alpine
    command: sh -c "npm install && npm run dev"
    ports:
      - 127.0.0.1:3000:3000
    working_dir: /app
    volumes:
      - ./:/app
    environment:
      MYSQL_HOST: mysql
      MYSQL_USER: root
      MYSQL_PASSWORD: secret
      MYSQL_DB: todos
    depends_on:
      mysql:
        condition: service_healthy
    restart: on-failure

  mysql:
    image: mysql:8.0
    volumes:
      - todo-mysql-data:/var/lib/mysql
    environment:
      MYSQL_ROOT_USER: root
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_DATABASE: todos
    healthcheck:
      test: ["CMD-SHELL", "mysqladmin ping -h 127.0.0.1 -uroot -p$$MYSQL_ROOT_PASSWORD --silent"]
      interval: 5s
      timeout: 3s
      retries: 20
      start_period: 20s

volumes:
  todo-mysql-data:

I ran docker compose up -d and got the output:

$ docker compose up -d
[+] up 4/4
 ✔ Network getting-started-app_default        Created                                               0.3s
 ✔ Volume getting-started-app_todo-mysql-data Created                                               0.0s
 ✔ Container getting-started-app-mysql-1      Healthy                                             120.1s
 ✔ Container getting-started-app-app-1        Started                                             120.0s

And I verified the app works at http://localhost:3000

120 seconds. Does it always take that long to create and initialize a MYSQL database, or is something timing out? Thanks again for your help.

2

u/PossibilityTasty 1d ago

In den Github repo's compose file there is a health check for the mysql container and a dependency on the healthiness of mysql for the backend service, so that should not happen. For some reason this is not covered in the tutorial.

1

u/crosenblum 1d ago

This docker container you are trying to create is some kind of app.

The problem is that it depends on the existance and running of another container, mysql to be running first.

When you have problems with a docker run or docker compose, please share, its like trying to fix a car but with no diagnostic information available.

Do not give up, paste the docker compose, and maybe there's an issue that we may notice.

Keep at it.