DEV Community

Roberto Luna
Roberto Luna

Posted on

Adding Per‑Container CPU & Memory Limits to Docker‑Compose for pgvector

Adding Per‑Container CPU & Memory Limits to Docker‑Compose for pgvector

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.


The Problem

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
Enter fullscreen mode Exit fullscreen mode

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.


What I Tried First

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
Enter fullscreen mode Exit fullscreen mode

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 Implementation

1. Update the Compose version

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: ...
Enter fullscreen mode Exit fullscreen mode

I bumped it to 2.4 to guarantee support for resource constraints:

version: "2.4"
services:
Enter fullscreen mode Exit fullscreen mode

2. Add 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: ...
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

3. Verify the limits

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)
Enter fullscreen mode Exit fullscreen mode

Running docker stats ismerely_postgres confirmed the caps:

CONTAINER ID   NAME                CPU %     MEM USAGE / LIMIT
c1d2e3f4g5h6    ismerely_postgres   0.12%     210MiB / 512MiB
Enter fullscreen mode Exit fullscreen mode

The PostgreSQL container now stays comfortably under its limit, and the other services no longer get OOM‑killed.

4. Why mem_limit vs 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.

5. Compatibility notes

  • Docker Engine ≥ 1.13 is required for mem_limit and cpus in Compose v2.4.
  • These keys are ignored by Docker Swarm (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).

Key Takeaway

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.


What's Next

  • Add healthchecks to the PostgreSQL service so Docker can automatically restart it if it becomes unresponsive.
  • Instrument container metrics with cAdvisor or Prometheus to monitor actual usage trends over time.
  • Explore dynamic limits: write a small script that adjusts mem_limit based on the size of the dataset loaded into pgvector during CI runs.

Roberto Luna Osorio – Full Stack Developer & Project Lead

Playa del Carmen, México

vibecoding #buildinpublic #docker #docker-compose #postgresql #pgvector #devops #resource-limits


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

Top comments (0)