Don’t Let Your Containers Race: How to Make Services Wait for MySQL in Docker Compose
In the world of Docker Compose, defining service dependencies is easy — just list them under depends_on
. But if you’ve ever run a PHP app, Directus, or Ghost CMS that talks to MySQL, you might have hit this problem:
“My app container started fine... but immediately crashed with a MySQL connection error.”
Why? Because depends_on
only waits for the container to start, not to be ready.
Let’s break this down — and solve it properly.
🧠 Why depends_on
Isn’t Enough
Here’s what a lot of people do:
services:
app:
build: .
depends_on:
- mysql
Looks fine, right? But Docker Compose interprets this as:
“Startmysql
before startingapp
.”
That’s all. It doesn’t care whether MySQL is done initializing, finished applying migrations, or even able to accept connections.
If your app hits the database too soon, it’s game over.
✅ Solution: Combine depends_on
With healthcheck
The real solution is a two-part approach:
1. Add a healthcheck
to your MySQL container:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: secret
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
This tells Docker, “Ping MySQL until it responds properly.”
2. Tell your app container to wait for a healthy MySQL:
app:
build: .
depends_on:
mysql:
condition: service_healthy
Now, Docker Compose will wait until MySQL passes the healthcheck
before launching the app container.
🛠 Bonus: Add wait-for-it.sh
or dockerize
for Extra Safety
Sometimes, even a healthcheck
isn't enough. Your app might still try to connect before the DB is truly ready — especially in complex setups.
You can add a wrapper script like wait-for-it.sh
or dockerize
to your app container.
Example using wait-for-it.sh
in your Dockerfile:
COPY wait-for-it.sh /usr/local/bin/
ENTRYPOINT ["wait-for-it.sh", "mysql:3306", "--", "npm", "start"]
This will pause your app startup until MySQL’s port is available.
⚠️ Things to Watch Out For
- No built-in retries: Most apps will not retry a failed DB connection unless explicitly coded to do so.
- Healthchecks are crucial for reliability, especially in CI or production setups.
- Beware of silent failures. Even if the containers are marked as “up”, they might still be broken internally.
🧩 Consider Using Init Containers (in Kubernetes)
If you’re planning to scale into Kubernetes, similar logic applies — but you’ll use init containers or startupProbe
instead of Compose’s depends_on
.
✨ Finally
Don’t let your containers race each other to start. Just because Docker says something is “up” doesn’t mean it’s actually ready. That’s a critical distinction when working with databases like MySQL or Postgres.
By combining:
depends_on
withcondition: service_healthy
- A proper
healthcheck
- And optionally, a startup wait script
…you can avoid those pesky “can’t connect to DB” errors and build a more reliable stack.
Comments ()