Skip to content

Alloy tails the backup archiver's tar stream into Loki #286

Description

@Gerrrt

Found while sizing memory limits for #114, and it is the single largest thing
driving the number that issue has to pick for loki.

What happens

loki.source.docker in stacks/observability/alloy/docker.alloy is fed by
discovery.docker "dockerlogs", which has no filter — it discovers every
container on the Docker socket, not just this stack's seven services. That
includes the short-lived containers docker run starts, and one of them writes
a gzip stream to stdout:

docker run --rm --network none --read-only ... "${TAR_IMAGE}" \
    tar --numeric-owner -czf - -C /data . | age --recipient ... --output "${out}"

scripts/backup-volumes.sh:520. The tar output is the container's stdout, so
the json-file driver records it and Alloy streams it to Loki as log lines.

Measured

make backup on 2026-08-29 (backups/volumes/20260829T064124Z/) put three
archiver containers through Loki:

container bytes ingested
goofy_perlman 269,627,560
fervent_lamport 269,305,584
relaxed_yonath 227,065,622

765 MB in about three minutes — sum(rate(loki_distributor_bytes_received_total[2m]))
peaked at 2.7 MiB/s against a baseline of 950 B/s. Sampling the stream back
out of Loki returns binary, as expected:

{service_name="goofy_perlman"}  ->  ?      ?]???D?nh??ݹ@B?$?Bnm?n93?+?@??@???8?

loki's working set went to 1039 MiB in that window against a 14-day p50 of
117 MiB; alloy reached 512 MiB and grafana 433 MiB at the same time. It is
not a one-off — the same shape, smaller, lands on the other backup timers:
589 MiB on 2026-09-04 (homelab-backup-firewall), 451 MiB on the
homelab-verify-backups run before it.

Daily Loki ingest averages 249 MiB, so one make backup is roughly three
days of the estate's real logs
, and it is retained as though it were logs.

Why it matters beyond the volume

  • It is the reason mem_limit on loki in Set memory limits on the six services to bound the blast radius of a leak #114 has to be 1536m rather than
    512m. Sizing a limit around a bug is the wrong shape, but the event is
    scheduled and recurring, so the limit has to survive it until this is fixed.
  • The archives are encrypted at rest with age, deliberately — the comment in
    archive_one() says the plaintext never touches the disk. The compressed
    plaintext
    does reach Loki, and loki-data is not encrypted. It is gzip, not
    cleartext, but it is a copy of every volume's contents sitting somewhere the
    backup design says it should not be.
  • Loki retention then evicts real logs earlier than it should.

Options

  • Filter discovery.docker to the stack — relabel on
    __meta_docker_container_label_com_docker_compose_project and drop what does
    not carry it. Narrowest, and it also stops every other ad-hoc docker run
    (the restoretest-*, offline-* and homelab-roundtrip-* containers CI
    starts) being tailed.
  • Or give the archiver logging: driver: none in compose.yaml. Narrower
    still, but it only fixes the container we happen to know about.

The first looks right; the second is worth doing anyway.

Done when

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions