123 lines
4 KiB
Markdown
123 lines
4 KiB
Markdown
---
|
|
title: "Docker Compose CPU Limits Break Per Environment"
|
|
aliases: [docker-cpu-limits, compose-cpu-env, cpus-range-error, docker-memory-reservation-limit]
|
|
tags: [docker, docker-compose, deployment, devops, gotcha]
|
|
sources:
|
|
- "daily/2026-04-29.md"
|
|
- "daily/2026-04-30.md"
|
|
created: 2026-04-29
|
|
updated: 2026-04-30
|
|
---
|
|
|
|
# Docker Compose CPU Limits Break Per Environment
|
|
|
|
Setting `cpus: '4.0'` in a prod `docker-compose.yml` and then running `docker compose up` on a server with only 2 CPUs causes a hard error. The fix is a per-environment compose override file that matches the target server's actual CPU count.
|
|
|
|
## Key Points
|
|
|
|
- Error message: `"range of CPUs is from 0.01 to 2.00"` — appears at `docker compose up`, NOT at `docker compose build`
|
|
- The build succeeds silently; the failure only surfaces at container startup
|
|
- prod compose is the canonical config; environment-specific files override resource limits only
|
|
- Naming convention: `docker-compose.{env}.yml` (e.g. `docker-compose.optical-dev.yml`)
|
|
|
|
## Details
|
|
|
|
The error occurs because Docker enforces CPU limits against the host's actual CPU count at container start time, not at image build time. A prod file with `cpus: '4.0'` is valid on a 4-core prod server but fails immediately on a 2-core staging server.
|
|
|
|
**Prod compose (canonical):**
|
|
|
|
```yaml
|
|
services:
|
|
ffmpeg-worker:
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
cpus: '4.0' # valid on 4-core prod
|
|
memory: 8G
|
|
```
|
|
|
|
**optical-dev override:**
|
|
|
|
```yaml
|
|
# docker-compose.optical-dev.yml
|
|
services:
|
|
ffmpeg-worker:
|
|
deploy:
|
|
replicas: 0 # also disable heavy workers on this env
|
|
resources:
|
|
limits:
|
|
cpus: '1.0' # optical-dev has 2 CPUs — keep 1 for other services
|
|
memory: 2G
|
|
```
|
|
|
|
**Deploy command using override:**
|
|
|
|
```bash
|
|
docker compose -f docker-compose.yml -f docker-compose.optical-dev.yml up -d
|
|
```
|
|
|
|
## Pattern
|
|
|
|
| File | Role |
|
|
|------|------|
|
|
| `docker-compose.yml` | Canonical config — prod values, all services defined |
|
|
| `docker-compose.prod.yml` | Prod-specific overrides (rarely needed if base IS prod) |
|
|
| `docker-compose.optical-dev.yml` | optical-dev overrides — reduced CPU/RAM, some services disabled |
|
|
| `docker-compose.override.yml` | Local dev — picked up automatically by Docker Compose |
|
|
|
|
## Memory Reservation > Limit Error Across Override Files
|
|
|
|
A related Docker Compose gotcha: Docker enforces `memory limit >= memory reservation` strictly at container start time. This breaks silently when override files set smaller limits without redefining reservations.
|
|
|
|
### Error Message
|
|
|
|
```
|
|
Error response from daemon: Minimum memory limit can not be less than memory reservation limit
|
|
```
|
|
|
|
### The Conflict
|
|
|
|
```yaml
|
|
# docker-compose.prod.yml — sets large reservations
|
|
services:
|
|
mongodb:
|
|
deploy:
|
|
resources:
|
|
reservations:
|
|
memory: 2G
|
|
|
|
# docker-compose.optical-dev.yml — reduces limits but forgets reservations
|
|
services:
|
|
mongodb:
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
memory: 1G # ERROR: 1G limit < 2G reservation inherited from prod
|
|
# reservations: not redefined — Docker inherits 2G from prod file
|
|
```
|
|
|
|
### Fix: Redefine Reservations in Every Override That Reduces Limits
|
|
|
|
```yaml
|
|
# docker-compose.optical-dev.yml
|
|
services:
|
|
mongodb:
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
memory: 1G
|
|
reservations:
|
|
memory: 512M # must be <= limit
|
|
```
|
|
|
|
The rule: **every override layer that changes memory limits must also set reservations ≤ new limit**.
|
|
|
|
## Related Concepts
|
|
|
|
- [[wiki/architecture/cloud-run-jobs-celery|cloud-run-jobs-celery]] — why optical-dev uses this pattern for heavy workers
|
|
- [[wiki/architecture/optical-dev-server-deploy|optical-dev-server-deploy]] — optical-dev server constraints and deployment pattern
|
|
|
|
## Sources
|
|
|
|
- [[daily/2026-04-29.md]] — session 20:29, `cpus: '4.0'` breaking on 2-CPU optical-dev server
|
|
- [[daily/2026-04-30.md]] — session 12:11, memory reservation > limit error in override files
|