Adding Per‑Container CPU & Memory Limits to Docker‑Compose for pgvector A developer fixed intermittent 502 errors and OOM kills in a local pgvector-based Docker Compose stack by adding per-container resource caps to the PostgreSQL service. The fix sets mem_limit: 512m and cpus: "1" on the pgvector/pgvector:pg16 container after discovering that the deploy.resources.limits block is silently ignored outside Swarm mode and that Compose file version 2.4 is required for the top-level keys to take effect. Docker inspect confirmed the 512 MiB memory and 1 CPU limits, and docker stats showed the container holding at roughly 210 MiB. TL;DR: I added mem limit and cpus to the PostgreSQL service in docker‑compose.yml to prevent the dev database from starving other containers. The change caps the container at 512 MiB RAM and 1 CPU, stabilizing the local environment without affecting production. Running the bienestar‑integral‑kb stack locally on my MacBook Pro M1, 16 GB RAM started to feel flaky after I added a couple of AI‑powered micro‑services. The PostgreSQL container pgvector/pgvector:pg16 would occasionally consume all available memory, causing the Docker daemon to kill other containers e.g., the FastAPI inference service . Docker Desktop shows the following symptom: 2026-10-10T14:32:12.345Z container is out of memory: kill process 12 postgres score 999 or sacrifice child The error manifested as intermittent 502 Bad Gateway responses from the API gateway because the dependent services were being OOM‑killed. The root cause was that Docker Compose, by default, gives each container unrestricted access to host resources. In a multi‑service dev environment that’s a recipe for “noisy neighbor” problems. My first instinct was to use Docker Desktop’s Resources → Advanced UI to limit the whole Docker engine to, say, 8 GB RAM. While that prevented total exhaustion, it also throttled services that legitimately needed more memory e.g., the embedding model . I needed a per‑container limit. I then tried the deploy.resources.limits block that Docker Compose supports: services: postgres: image: pgvector/pgvector:pg16 deploy: resources: limits: cpus: "1" memory: 512M Unfortunately, the deploy key is ignored unless the Compose file is run in Swarm mode docker stack deploy . Running docker compose up the v2 CLI silently ignored those limits, leaving the problem unchanged. The next approach was to switch the Compose file version to 3.8 and use the mem limit and cpus top‑level keys, which are supported in version 2.x files. After a quick read of the docs, I realized that Compose file version 2.4 or later allows these fields without Swarm. That’s the path I took. The original docker-compose.yml started with version 2 : version: "2" services: postgres: image: pgvector/pgvector:pg16 container name: ismerely postgres restart: unless-stopped environment: POSTGRES USER: ... I bumped it to 2.4 to guarantee support for resource constraints: version: "2.4" services: mem limit and cpus The diff from commit 00f0bbc2 shows the exact lines added: @@ -4,6 +4,8 @@ services: image: pgvector/pgvector:pg16 container name: ismerely postgres restart: unless-stopped + mem limit: 512m + cpus: "1" environment: POSTGRES USER: ... Full updated service definition: services: postgres: image: pgvector/pgvector:pg16 container name: ismerely postgres restart: unless-stopped mem limit: 512m cap RAM at 512 MiB cpus: "1" limit to a single logical CPU environment: POSTGRES USER: ${POSTGRES USER} POSTGRES PASSWORD: ${POSTGRES PASSWORD} POSTGRES DB: ${POSTGRES DB} volumes: - pg data:/var/lib/postgresql/data ports: - "5432:5432" After docker compose up -d , I inspected the container: bash $ docker inspect ismerely postgres --format='{{json .HostConfig.Memory}}' | jq . 536870912 512 MiB in bytes $ docker inspect ismerely postgres --format='{{json .HostConfig.NanoCpus}}' | jq . 1000000000 1 CPU 1e9 nanocpus Running docker stats ismerely postgres confirmed the caps: CONTAINER ID NAME CPU % MEM USAGE / LIMIT c1d2e3f4g5h6 ismerely postgres 0.12% 210MiB / 512MiB The PostgreSQL container now stays comfortably under its limit, and the other services no longer get OOM‑killed. mem reservation I considered using mem reservation soft limit but opted for a hard cap mem limit because the dev environment is already memory‑constrained. A soft limit would still allow the DB to balloon under load, re‑triggering the original issue. docker stack deploy . If the project ever migrates to Swarm or Kubernetes, you’ll need to translate the constraints to the respective platform’s syntax deploy.resources.limits for Swarm, resources.limits in a K8s pod spec . Per‑container resource constraints in Docker Compose are a lightweight, production‑agnostic way to prevent a single service from monopolizing host resources during local development. Using mem limit and cpus available from Compose file version 2.4 gives you deterministic behavior without the overhead of Swarm or Kubernetes. cAdvisor or Prometheus to monitor actual usage trends over time. Roberto Luna Osorio – Full Stack Developer & Project Lead Playa del Carmen, México Part of my Build in Public https://dev.to/zaerohell series — sharing the real process of building Building Ismerely KB from Playa del Carmen, México. Repo: zaerohell/bienestar-integral-kb · 2026-10-11 playadev buildinpublic