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

> Source: <https://dev.to/zaerohell/adding-per-container-cpu-memory-limits-to-docker-compose-for-pgvector-1nh9>
> Published: 2026-10-12 22:18:04+00:00

**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
