Docker: my commands and a few rakes I stepped on

Commands I keep coming back to, plus the mistakes I keep making.

Command order

The syntax once confused me too: flags control docker, arguments control the container.

# docker <management> <command> <docker flags> <name> <image> <container command>
docker container run -d --name web --restart unless-stopped -p 80:80 -v /data:/srv:ro caddy:2-alpine
docker run -it --rm -v /opt:/mnt:ro alpine sh   # classic for peeking at host folders
docker exec -it web sh                          # get inside a running container
docker exec -u runner -it web sh                # as a specific user — important when debugging permissions

Compose is similar, but has its own rakes: a path in volumes: resolves relative to the compose file — we fought this in production just the other day.

Logs

docker logs -f web              # follow live
docker logs --tail 100 web      # last 100 lines
docker logs --since 10m web     # last 10 minutes
docker compose -f docker-compose.prod.yml logs -f web

Cleaning up

The most common cause of "disk full" on a host with CI runners:

docker system df                    # what's taking space at all
docker system prune -f              # dangling containers, networks, old build cache
docker system prune -af             # PLUS all unused images — careful, you'll pull from there again
docker builder prune -af --filter until=72h   # buildkit cache older than 3 days — safer
docker volume prune -f              # dangling volumes: run df first, then this

How I keep runner bloat in check

GitLab runners write image caches and build layers non-stop, and the disk silently fills. My measures:

  1. Forced cleanup after every deploy — in the image build job's after_script: docker image prune -f && docker builder prune -af --filter until=72h
  2. A systemd timer (not cron, so missed runs catch up):
    systemctl enable --now docker-cleanup.timer
    
    # /etc/systemd/system/docker-cleanup.timer
    [Timer]
    OnCalendar=daily
    Persistent=true
    [Install]
    WantedBy=timers.target
    
    And in the service: docker system prune -f && docker builder prune -af --filter until=168h.
  3. Monitoring: alert when / goes past 80%. Disks always fill up on a Friday evening — Murphy's law never sleeps.

Also: docker stats is the first thing I check when a host feels sluggish. Shows who's eating CPU and memory right now.