Stop Docker Logs From Filling the Disk on a Django Host

Stop Docker container logs from filling the disk on a Django server: find the large json.log files, set max-size and max-file in daemon.json or Compose, use the local driver, and reduce noisy logging.

Problem statement

The server's disk reached 100%, PostgreSQL stopped accepting writes, and the culprit is under /var/lib/docker/containers/:

$ sudo du -sh /var/lib/docker/containers/*/*-json.log | sort -h | tail -3
2.1G  /var/lib/docker/containers/4f1c.../4f1c...-json.log
9.8G  /var/lib/docker/containers/a7e2.../a7e2...-json.log
31G   /var/lib/docker/containers/c03d.../c03d...-json.log

Docker's default json-file logging driver keeps everything a container writes to stdout and stderr, forever, in one growing file per container. A Django app logging every request, a Celery worker at DEBUG level, or a crash loop printing tracebacks can fill a disk in days. Docker does not rotate these logs unless you tell it to.

How container logs fill a diskApp writes stdoutevery request, taskjson-file driverappends to one fileNo rotationdefault is unlimitedDisk fulldatabase stopsDocker keeps container logs forever by default; rotation must be configured explicitly.
  1. Find the biggest files: sudo du -sh /var/lib/docker/containers/*/*-json.log | sort -h | tail.
  2. Free space now: sudo truncate -s 0 <file> (do not rm a file Docker holds open).
  3. Set rotation globally in /etc/docker/daemon.json: "log-opts": {"max-size": "20m", "max-file": "5"}.
  4. Restart Docker, then recreate containers; existing containers keep their old settings.
  5. Reduce volume at the source: log level INFO, no per-request noise from health checks.

Step-by-step solution

1. Identify which container

sudo du -sh /var/lib/docker/containers/*/*-json.log | sort -h | tail -5
docker ps -a --no-trunc --format '{{.ID}} {{.Names}}' | grep c03d

Map the directory ID back to a container name. Check what it logs: docker logs --tail 50 <name>.

2. Free space safely

sudo truncate -s 0 /var/lib/docker/containers/c03d.../c03d...-json.log
df -h /

truncate empties the file while Docker keeps writing to it. Deleting it with rm leaves the space allocated until the container stops, because the daemon still has the file open.

3. Configure rotation for every container

{
  "log-driver": "local",
  "log-opts": {
    "max-size": "20m",
    "max-file": "5"
  }
}

Save as /etc/docker/daemon.json, then:

sudo systemctl restart docker
docker compose up -d --force-recreate

The local driver stores logs in a compressed, rotated format and still works with docker logs. If you prefer json-file (some log shippers read those files directly), keep "log-driver": "json-file" with the same log-opts. Either way, each container is capped at about 100 MB here.

Daemon defaults apply only to containers created after the restart. Existing containers keep the configuration they were created with until recreated.

one busy web container after 30 days (illustrative)json-file, default31 GBjson-file, 20m x 50.1 GB caplocal, 20m x 5compressed0 GB16 GB32 GBRotation turns unbounded growth into a fixed ceiling per containerdjango-deployment.com
Illustrative numbers: cap every container, then ship logs elsewhere if you need history.

4. Or set it per service in Compose

x-logging: &default-logging
  driver: local
  options:
    max-size: "20m"
    max-file: "5"

services:
  web:
    logging: *default-logging
  worker:
    logging: *default-logging

Per-service settings travel with the project, which helps on servers where you do not control daemon.json.

5. Log less at the source

Rotation caps disk use; reducing noise makes the remaining logs useful:

  • Run Django and Celery at INFO, not DEBUG, in production.
  • Exclude health checks from Gunicorn access logs or the proxy's logs; a check every 5 seconds from three sources is 50,000 lines a day.
  • Fix crash loops instead of letting them print the same traceback every second.
  • Ship logs to a central system (Loki, CloudWatch, Datadog) if you need retention beyond the local cap.
bounded logs on a Docker hostdaemon.json sets max-size and max-fileor local driverContainers recreated after the changeold ones keep old settingsApp logs at INFO in productionno DEBUG noiseHealth checks excluded from access logshuge line countsDisk usage alert at 80 percentbefore the database stopsRotation plus an alert means a noisy container never takes the host downdjango-deployment.com
The alert catches whatever the rotation settings do not.

Verification check:

docker inspect --format '{{.HostConfig.LogConfig}}' $(docker ps -q)

Every running container should show your driver and options. Check df -h and the log directory sizes again after a week.

Explanation

Containers are expected to log to stdout and stderr, and Docker captures those streams through a logging driver. The default json-file driver writes each line as a JSON object to a file per container, with no size limit unless max-size is set. That default exists for compatibility, but it means a long-running container's log only ever grows. Docker removes the file only when the container is removed, so a container that runs for months keeps months of output.

Edge cases / notes

  • docker logs with syslog or journald drivers works only for some drivers; local and json-file support it, syslog does not on older versions.
  • journald driver stores logs in the system journal, so set SystemMaxUse= in journald.conf instead.
  • Build cache and images also fill disks; docker system df shows them, and docker builder prune reclaims cache.
  • Volumes with database data are not affected by log settings; never prune volumes casually.

Next steps

FAQ

Does Docker rotate container logs by default?

No. The default json-file driver has no size limit unless you set max-size.

Can I delete the json.log file?

Truncate it instead. Deleting it does not free space while the container is running.

Why did my daemon.json change not apply?

Existing containers keep their log settings. Recreate them after restarting Docker.

Which driver should I use?

local for compact rotated logs on the host, or json-file with limits if a shipper reads those files.

Keep going