How do the json-file driver's max-size and max-file log options stop a Docker host filling with logs?
answer
- Two options, and one is useless alone
- The driver rotates itself, no cron involved
- Per container, so multiply by container count
- One driver ships with sane defaults already
- Applied at creation, so recreate to change
basics
~20 smax-size caps one log file and max-file caps how many rotated copies are kept, bounding a container at roughly max-size times max-file. The json-file driver applies neither by default, and max-file does nothing unless max-size is set.
solid answer
~50 sThe **json-file** driver rotates its own files — there is no `logrotate` involved. `max-size` (for example `10m`) is the point at which the current file is rolled and a new one started; `max-file` is how many files are kept, oldest deleted first. Together they bound one container at roughly `max-size × max-file`. The trap is the defaults: json-file sets **no** size limit, so a single container can write a multi-gigabyte file, and `max-file` alone does nothing unless `max-size` is set as well. Set them in `/etc/docker/daemon.json` under `log-opts`, or per container with `--log-opt`. Because log configuration is frozen at container creation, changing the daemon default fixes only containers created afterwards — existing ones must be recreated. The **local** driver is the shortcut: it rotates at 20 MB across 5 compressed files out of the box.
go deeper
Know that container logs can fill a host and that max-size and max-file are the options that stop it. Being able to name where the files live and that the defaults are unlimited is enough at this level.
Explain that the driver rotates its own files, that max-file is inert without max-size, that the bound is per container, and how the local driver's defaults differ from json-file's.
Demonstrate the operational reality: the settings apply only to containers created after the change, so a fix means recreating containers, and rotation is a disk safety valve rather than retention you can investigate with.
Own the host disk budget: a per-container bound multiplied across the fleet, enforced as a default rather than left to each team, plus a decision about where logs go once you accept the local window is minutes long.
## The failure this prevents A fleet of geospatial tile servers — Django behind gunicorn — ran with the engine defaults and an access log on every tile request. Seventeen containers on one host, several weeks uptime, and `/var/lib/docker` sat at 41 GB with individual `-json.log` files past 3 GB. Nothing had misbehaved: json-file did exactly what it is configured to do out of the box, which is to append forever. That is the whole point of `max-size` and `max-file`. They are not an optimisation; they are the only thing standing between a chatty container and a full disk. ## How rotation works Rotation is performed by the driver inside `dockerd`, not by the host's `logrotate`. When the active file reaches `max-size`, the driver renames it with a numeric suffix, shifts the older ones along, starts a fresh active file, and deletes anything beyond `max-file`. The steady-state footprint of one container is therefore bounded at about `max-size × max-file`. Two behaviours surprise people: - **`max-file` alone does nothing.** With no `max-size` there is never a rotation event, so there is never a second file to count. The pair must be set together. - **The bound is per container, not per host.** `max-size=10m, max-file=3` means 30 MB *each*. On the tile-server host that is still 510 MB of logs, which is fine — but the arithmetic is yours to do. Sizing is a host-level budget: number of containers × the per-container bound, with headroom for the images and writable layers sharing the same filesystem. ## Defaults, and the driver that has sane ones The **json-file** driver defaults to an unlimited size, a single file, and no compression. The **local** driver was added precisely to fix this: it rotates at **20 MB** across **5** files and compresses the rotated ones by default, and stores the same information in a more compact binary format instead of escaped JSON. If your only requirement is "logs on the host, bounded, readable with `docker logs`", `local` is the better default and needs no options at all. Compression is available on json-file too (`compress`, off by default). It is a real saving on text logs, at the cost of CPU when rotating and a decompression step when the file is read back. ## Configuring it Daemon-wide, in `/etc/docker/daemon.json`: ```json { "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3", "compress": "true" } } ``` Per container, when one workload needs a different budget: ``` docker run -d --log-opt max-size=64m --log-opt max-file=4 tileserv:2.4 ``` The timing rule matters more here than anywhere else: **the log configuration is resolved when the container is created**, so editing `daemon.json` and restarting the engine leaves every existing container exactly as unbounded as it was. On a host that is already in trouble, the fix is to recreate the containers with the new options — the new settings do not retroactively trim files that have already been written. ## What rotation is not Rotation is a *disk safety valve*, not a retention policy. With `10m × 3` and a busy access log at a few hundred lines a second, the oldest surviving line may be twenty minutes old — which is worse than useless during a post-incident review the next morning. If logs matter beyond the immediate present they have to leave the host, either by a collector reading these files or by a shipping driver; rotation then only has to survive the collector being briefly behind. Rotation also does not apply to every driver. With **journald**, size and retention are the journal's business and are configured in systemd, not with `max-size`. With a shipping driver such as fluentd or awslogs, nothing accumulates on the host in the first place — retention belongs to whatever receives the messages. A final operational note: these files are held open by the daemon, so deleting the active log file out from under a running container does not behave the way you would hope, and reclaiming space that has already been consumed is a separate exercise from bounding future growth. The durable fix is always the bound, applied to every container that gets created. ## Making the bound hold Because the setting is inherited from the daemon default at creation time, the reliable place to enforce it is `daemon.json` on every host, not the run command each team writes. A bound that has to be remembered will be forgotten by exactly the service that needs it, and the omission is invisible until the disk is full. Two things are worth checking periodically on a host you care about: that each running container's effective log configuration really carries a limit, and that the total the fleet is entitled to still fits the filesystem holding Docker's data root, which also carries images, writable layers and volumes. Rotation protects you from a chatty container; it does not protect you from having promised more bytes than the disk has.
- You set max-file=5 but no max-size, and the log still grows without limit. Why?`max-file` only counts rotated files, and rotation is triggered by size. With no `max-size` the driver never rolls the active file, so there is only ever one file and nothing for `max-file` to bound. The two options must be set together; setting `max-file` alone is one of the most common silent misconfigurations of the json-file driver.
- How would you pick max-size and max-file for a specific service?Start from the log rate: lines per second times average line size gives bytes per second. Decide how much local history you want in the worst case — usually enough to cover a collector outage or the time it takes an on-call engineer to look — and divide. Then multiply the per-container bound by the number of containers on the host and check it against the disk budget for Docker's data root, leaving headroom for images and volumes.
- Why might you choose the local driver over json-file with the same options set explicitly?`local` is bounded out of the box (20 MB across 5 compressed files), so a container created before anyone edited `daemon.json` is still safe. It also stores messages in a compact binary format rather than escaped JSON, which is meaningfully smaller for the same content. You keep `docker logs` either way. json-file's advantage is only that its on-disk format is trivially greppable by other tools.
saying these in an interview costs you the question
- Says logrotate on the host handles container logs
- Sets max-file without max-size and expects a bound
- Thinks json-file rotates at some sensible default size
- Treats the bound as per host rather than per container
- Believes new daemon.json options apply to existing containers
- Calls rotation a log retention policy