cd /news/ai-infrastructure/adding-per-container-cpu-memory-limi… · home › topics › ai-infrastructure › article
[ARTICLE · art-149888] src=dev.to ↗ pub= topic=ai-infrastructure verified=true sentiment=↑ positive

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.

by read3 min views1 publishedOct 12, 2026

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

── more in #ai-infrastructure 4 stories · sorted by recency
── more on @docker compose 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/adding-per-container…] indexed:0 read:3min 2026-10-12 · —