DEV Community

Alex Spinov
Alex Spinov

Posted on

Your Docker Compose File Is Probably Wrong — 7 Mistakes I See in Every Project

I've reviewed over 200 Docker Compose files. Most of them have the same problems.

Not bugs — they work fine. But they're ticking time bombs for production, security, or your sanity at 3 AM.

Here are the 7 most common mistakes and how to fix them.

1. Using latest Tag

# BAD
services:
  db:
    image: postgres:latest

# GOOD
services:
  db:
    image: postgres:16.2-alpine
Enter fullscreen mode Exit fullscreen mode

Why it matters: latest today is not latest tomorrow. Your staging and production will run different versions. Debugging will be hell.

Rule: Always pin to a specific version. Use Alpine variants for smaller images.

2. No Health Checks

# BAD
services:
  api:
    depends_on:
      - db

# GOOD  
services:
  db:
    image: postgres:16.2-alpine
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5

  api:
    depends_on:
      db:
        condition: service_healthy
Enter fullscreen mode Exit fullscreen mode

Why it matters: depends_on only waits for the container to start, not for the service to be ready. Your API crashes because Postgres isn't accepting connections yet.

3. Hardcoded Credentials

# BAD
services:
  db:
    environment:
      POSTGRES_PASSWORD: mysecretpassword123

# GOOD
services:
  db:
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    env_file:
      - .env
Enter fullscreen mode Exit fullscreen mode

And add .env to .gitignore. I've found production database passwords in public GitHub repos more times than I'd like to admit.

4. No Resource Limits

# BAD — container can eat all your RAM
services:
  api:
    image: myapp

# GOOD
services:
  api:
    image: myapp
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: '0.5'
Enter fullscreen mode Exit fullscreen mode

Why it matters: One runaway container shouldn't kill your entire server. Set limits based on actual usage (check with docker stats).

5. Not Using Named Volumes

# BAD — bind mount
services:
  db:
    volumes:
      - ./data:/var/lib/postgresql/data

# GOOD — named volume
services:
  db:
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:
Enter fullscreen mode Exit fullscreen mode

Why it matters: Bind mounts have permission issues across OS. Named volumes are managed by Docker, portable, and easier to backup.

Use bind mounts only for development (code sync). Use named volumes for data.

6. Running as Root

# In your Dockerfile
# BAD
CMD ["node", "server.js"]

# GOOD
RUN addgroup -S app && adduser -S app -G app
USER app
CMD ["node", "server.js"]
Enter fullscreen mode Exit fullscreen mode

In compose:

services:
  api:
    user: "1000:1000"
Enter fullscreen mode Exit fullscreen mode

Why it matters: If your container gets compromised, the attacker has root access to the container filesystem. Running as non-root limits the blast radius.

7. No Restart Policy

# BAD — container dies, stays dead
services:
  api:
    image: myapp

# GOOD
services:
  api:
    image: myapp
    restart: unless-stopped
Enter fullscreen mode Exit fullscreen mode

Options:

  • no — never restart (default)
  • always — restart no matter what
  • on-failure — restart only on non-zero exit
  • unless-stopped — restart unless you explicitly stopped it

For production: unless-stopped. For dev: no or on-failure.

Bonus: The Template

I maintain a collection of battle-tested Docker Compose configs:

Docker Compose Templates — PostgreSQL, Redis, MongoDB, Elasticsearch, Grafana, MinIO. Copy-paste and go.

More DevOps tools: GitHub Actions Templates | Awesome Developer Tools 2026


What's the worst Docker mistake you've seen in production? I've got stories. 👇

DevOps articles at dev.to/0012303

Top comments (0)