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:
$ 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 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