apple / apple/container

[Performance]: Postgres workload reports much higher cgroup/runtime memory than Docker despite similar process RSS

Open
#1,698 0 comments 0 reactions 1 assignee Claimed by @jglogan View on GitHub
Dominant language
Swift
Stars
49.9k
Forks
1.8k
Avg merge
1d 20h
Merged PRs (30d)
22

Description

### I have done the following

- [x] I have searched the existing issues
- [ ] If possible, I've reproduced the issue using the `main` branch of this project

### Steps to reproduce

I measured a public Postgres workload repeatedly on the same machine against Docker/OrbStack and Apple `container`.

Environment:

- macOS 26.5.1 (25F80), arm64
- Xcode 26.5 (17F42)
- Apple `container` CLI 1.0.0 (release, commit `ee848e3`)
- MacBook Pro Mac14,7, Apple M2, 16 GB memory
- Docker/OrbStack baseline available on the same machine

Apple workload shape:

1. Start Apple `container` services.
2. For each iteration, create a fresh named volume.
3. Run `docker.io/library/postgres:16-alpine` with:
- `POSTGRES_USER=app`
- `POSTGRES_PASSWORD=`
- `POSTGRES_DB=app`
- `PGDATA=/var/lib/postgresql/data/pgdata`
- volume mounted at `/var/lib/postgresql/data`
4. Wait for `pg_isready`.
5. Capture:
- `container stats --no-stream`
- `/proc/1/status` inside the container
- cgroup `memory.current`, `memory.peak`, and `memory.max`
- `du -sk /var/lib/postgresql/data`
- runtime-reported block I/O
6. Run a short synthetic SQL loop using `generate_series`.
7. Stop/delete the container and delete the named volume.

Docker/OrbStack used the same image, fresh volume-per-iteration shape, and the
same process/cgroup/disk probes where available.

Iteration counts:

| Workload | Docker/OrbStack | Apple `container` |
| --- | ---: | ---: |
| `postgres-db-only` | 50 | 20 |
| backend-shaped Postgres role | 20 | 10 |

### Problem description

For a small Postgres workload, the Postgres process RSS and data footprint are effectively the same between Docker/OrbStack and Apple `container`, but Apple reports much higher runtime/cgroup memory and higher DB block reads.

DB-only memory:

| Runtime | Process RSS p50 | Cgroup current p50 | Runtime memory p50 |
| --- | ---: | ---: | ---: |
| Docker/OrbStack | 26.68 MiB | 65.14 MiB | 17.21 MiB |
| Apple `container` | 26.57 MiB | 187.45 MiB | 187.01 MiB |

Backend-shaped DB role memory:

| Runtime | Process RSS p50 | Cgroup current p50 | Runtime memory p50 |
| --- | ---: | ---: | ---: |
| Docker/OrbStack | 26.65 MiB | 67.33 MiB | 20.09 MiB |
| Apple `container` | 26.57 MiB | 188.45 MiB | 188.15 MiB |

Disk footprint:

| Workload | Runtime | Data footprint p50 |
| --- | --- | ---: |
| DB-only | Docker/OrbStack | 45.70 MiB |
| DB-only | Apple `container` | 45.70 MiB |
| Backend DB | Docker/OrbStack | 45.78 MiB |
| Backend DB | Apple `container` | 45.79 MiB |

Block read snapshots:

| Workload | Runtime | Block read p50 |
| --- | --- | ---: |
| DB-only | Docker/OrbStack | 0.00 MiB |
| DB-only | Apple `container` | 81.05 MiB |
| Backend DB | Docker/OrbStack | 3.09 MiB |
| Backend DB | Apple `container` | 81.05 MiB |

I expected some overhead from Apple `container`'s one-VM-per-container model, but the process RSS being the same while cgroup/runtime memory is roughly 187-188 MiB makes it hard to tell whether this is:

- expected per-container VM/runtime overhead;
- cgroup accounting that includes runtime/kernel/init memory;
- a tunable default that should be documented for DB/backend workloads;
- or an optimization opportunity.

Could maintainers clarify whether this memory profile is expected, and whether there are recommended flags or runtime settings for small DB containers where the process RSS is much lower than the reported cgroup/runtime memory?

### Environment

- OS: macOS 26.5.1 (25F80), arm64
- Xcode: 26.5 (17F42)
- Container: `container CLI version 1.0.0 (build: release, commit: ee848e3)`
- Hardware: MacBook Pro Mac14,7, Apple M2, 16 GB memory

### Notes

This came from a Compose-adapter feasibility benchmark. The backend-shaped Apple path used documented workarounds in my harness:

- `PGDATA=/var/lib/postgresql/data/pgdata`, because mounting a named volume directly at the Postgres data root exposed `lost+found`.
- DB-IP targeting instead of Compose-style service-name DNS.

So the backend-shaped result is not being presented as Compose parity. The DB-only result is the narrower signal: same Postgres image, same fresh-volume shape, same process RSS, same data footprint, but much higher Apple cgroup/runtime memory and block-read snapshots.

### Additional context: lower-level LinuxPod path

This does not appear to be only a `container` CLI surface question in my local investigation.

I also tested lower-level `apple/containerization` / `LinuxPod` paths for a Compose-like local-development runtime. Those experiments did not establish a more efficient backend-shaped path yet: warm/rootfs-cache flows still required rootfs materialization work, post-create `LinuxPod.addContainer` hotplug remained unsupported for a real ext4/block rootfs, and the valid runtime evidence did not justify a Docker/OrbStack parity or host-memory-efficiency claim.

So my current interpretation is narrower than "CLI stats formatting is confusing": the higher reported cgroup/runtime memory in this issue may be part of a broader runtime/accounting/VM-overhead question across the Apple container stack.

I am not claiming the LinuxPod evidence is the same benchmark as the CLI numbers above, but it did not give me a more efficient lower-level escape hatch for backend-shaped local-dev workloads.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.