How to Use Logspout to Collect Container Logs: Centralized Docker Logging Setup

How to Use Logspout to Collect Container Logs

The first time I had more than five containers running on a single host, docker logs container-name stopped being a workflow and became a chore. Tailing logs one container at a time, across multiple hosts, during an incident is exactly the kind of thing that turns a 10-minute debugging session into an hour. Logspout solved that for me — it’s a lightweight log router that attaches to the Docker socket, tails stdout/stderr from every running container, and ships it wherever you want: syslog, a HTTP endpoint, Logstash, Elasticsearch, or a custom adapter. This guide covers setting it up from scratch, understanding how it works internally, and routing logs to a real aggregation backend.

What Logspout Actually Does

Logspout is not a log storage system — it’s a router. It:

  1. Connects to the Docker daemon’s Unix socket (/var/run/docker.sock).
  2. Discovers all running containers and attaches to their log streams (the same stream docker logs reads from).
  3. Watches for new containers starting/stopping and attaches/detaches dynamically — no restart needed.
  4. Forwards each log line, tagged with container metadata, to one or more configured destinations (“routes”).

Because it reads directly from the Docker log driver, it works with any container without requiring you to modify application code to log to a specific place.

Prerequisites

  • Docker Engine installed and running.
  • Root or a user in the docker group (Logspout needs access to /var/run/docker.sock).

Check your Docker version:

docker version --format '{{.Server.Version}}'

Step 1: Run Logspout With a Basic Route

The simplest setup forwards all container logs to a syslog endpoint. Let’s start with an actual syslog server for testing — papertrail-style or a local syslog-ng container works, but for a first test we’ll just print to a local UDP listener.

docker run -d --name logspout \
  --volume=/var/run/docker.sock:/var/run/docker.sock \
  --publish=127.0.0.1:8000:80 \
  gliderlabs/logspout

This starts Logspout with its HTTP API exposed on port 8000 (useful for live-tailing without a destination configured yet).

Verify it’s running:

docker ps --filter name=logspout

Expected output:

CONTAINER ID   IMAGE                  STATUS         PORTS                      NAMES
a1b2c3d4e5f6   gliderlabs/logspout    Up 5 seconds   0.0.0.0:8000->80/tcp      logspout

Step 2: Live-Tail All Container Logs Over HTTP

Logspout exposes a /logs streaming endpoint out of the box:

curl http://127.0.0.1:8000/logs

Start another container to generate some log output:

docker run -d --name test-app alpine sh -c "while true; do echo 'hello from test-app'; sleep 2; done"

Back in your curl terminal, expected output:

test-app|hello from test-app
test-app|hello from test-app

This confirms Logspout picked up the new container automatically — no configuration change or restart required, which is the core value proposition versus manually configuring log shipping per container.

Step 3: Route Logs to Syslog (Real Aggregation)

Most production setups forward to a centralized syslog receiver, Logstash, or a managed log service. The route is passed as a command argument using the syslog:// scheme:

docker run -d --name logspout \
  --volume=/var/run/docker.sock:/var/run/docker.sock \
  gliderlabs/logspout \
  syslog://logs.example.com:514

For local testing, stand up a syslog receiver container:

docker run -d --name syslog-server -p 514:514/udp balabit/syslog-ng:latest

Then point Logspout at it via the Docker network (assuming both are on the same user-defined network, logging-net):

docker network create logging-net

docker run -d --name syslog-server \
  --network logging-net \
  -p 514:514/udp \
  balabit/syslog-ng:latest

docker run -d --name logspout \
  --network logging-net \
  --volume=/var/run/docker.sock:/var/run/docker.sock \
  gliderlabs/logspout \
  syslog://syslog-server:514

Step 4: Filter Which Containers/Logs Get Shipped

By default Logspout ships stdout+stderr from every container, including itself. You almost always want to exclude some containers (e.g., the log shipper itself, or noisy health-check sidecars). Use environment variables or route-level filters:

docker run -d --name logspout \
  --network logging-net \
  --volume=/var/run/docker.sock:/var/run/docker.sock \
  -e EXCLUDE_LABEL=logspout.exclude \
  gliderlabs/logspout \
  syslog://syslog-server:514

Then label containers you want excluded:

docker run -d --name noisy-healthcheck \
  --label logspout.exclude=true \
  alpine sh -c "while true; do echo ping; sleep 1; done"

You can also filter by source stream:

gliderlabs/logspout syslog://syslog-server:514?filter.sources=stdout

This ships only stdout, excluding stderr — useful if you route error logs somewhere different.

Step 5: Route to Multiple Destinations at Once

Logspout supports multiple simultaneous routes, comma-separated:

docker run -d --name logspout \
  --network logging-net \
  --volume=/var/run/docker.sock:/var/run/docker.sock \
  gliderlabs/logspout \
  syslog://syslog-server:514,http://logstash.example.com:8080

This is useful during a migration — ship to your old syslog target and a new ELK pipeline simultaneously while you validate the new one.

Step 6: Docker Compose Setup for a Real Stack

A realistic logging stack pairs Logspout with Logstash/Elasticsearch/Kibana (the classic ELK approach) or a lighter Loki-based stack. Here’s Logspout feeding Logstash:

# docker-compose.yml
version: "3.8"

networks:
  logging-net:
    driver: bridge

services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.14.0
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false
      - ES_JAVA_OPTS=-Xms512m -Xmx512m
    networks:
      - logging-net
    ports:
      - "9200:9200"

  logstash:
    image: docker.elastic.co/logstash/logstash:8.14.0
    volumes:
      - ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf
    networks:
      - logging-net
    depends_on:
      - elasticsearch
    ports:
      - "5000:5000"

  kibana:
    image: docker.elastic.co/kibana/kibana:8.14.0
    networks:
      - logging-net
    ports:
      - "5601:5601"
    depends_on:
      - elasticsearch

  logspout:
    image: gliderlabs/logspout
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    networks:
      - logging-net
    command: logstash://logstash:5000
    depends_on:
      - logstash

  app:
    image: nginx:alpine
    networks:
      - logging-net

logstash.conf:

input {
  tcp {
    port => 5000
    codec => json_lines
  }
}

output {
  elasticsearch {
    hosts => ["http://elasticsearch:9200"]
    index => "docker-logs-%{+YYYY.MM.dd}"
  }
}
docker compose up -d

After a minute, check that logs landed in Elasticsearch:

curl -s http://localhost:9200/_cat/indices?v

Expected output:

health status index                  uuid    pri rep docs.count
yellow open   docker-logs-2026.07.29 xxxxxx  1   1   142

Open Kibana at http://localhost:5601 and create a data view over docker-logs-* to search and visualize.

How Logspout Works Internally

Under the hood, Logspout uses the Docker Engine API’s container attach/logs endpoint with follow=true, which streams multiplexed stdout/stderr frames over the socket connection. Docker’s log driver framing includes a stream-type byte (stdout vs stderr) and a length-prefixed payload — Logspout demultiplexes this, attaches container metadata (name, ID, image, labels), and hands each line off to whichever output adapter matches your configured route scheme (syslog://, http://, logstash://, or a custom Go adapter you register at build time).

Because it uses the logs API rather than reading log files directly, it works regardless of which logging driver the container itself is configured with (json-file, local, etc.), as long as that driver supports the read-back API — note that drivers like none or some remote-only drivers won’t have anything for Logspout to read.

Security Considerations

  • Docker socket access is root-equivalent. Mounting /var/run/docker.sock into Logspout gives it the same power as root on the host. Only run trusted, pinned images (use a specific tag/digest, not latest, in production), and don’t expose Logspout’s HTTP API (8000) publicly.
  • Encrypt log transport in production — plain syslog:// over UDP is unauthenticated and unencrypted; use syslog+tls:// where supported, or route through a TLS-terminating aggregator.
  • Scrub sensitive data (tokens, PII) at the application log-emission level — Logspout ships whatever containers print; it does not redact.

Troubleshooting

SymptomCauseFix
No logs appearing at destinationRoute URL/scheme typo, or destination unreachableCheck docker logs logspout for connection errors
Logspout can’t start / permission deniedDocker socket not mounted or wrong permissionsConfirm -v /var/run/docker.sock:/var/run/docker.sock and that the user has socket access
Only some containers’ logs show upEXCLUDE_LABEL or filter.sources misconfiguredRemove filters temporarily to confirm baseline behavior
Duplicate log lines at destinationMultiple Logspout instances or restart loopsEnsure only one Logspout container per host; check docker ps -a for crash-looping instances

Check Logspout’s own logs for adapter errors:

docker logs logspout --tail 50

Real-World Deployment Notes

I’ve run Logspout as a single instance per host in a multi-host fleet, each one shipping to a shared aggregation cluster rather than trying to centralize the shipper itself — it’s stateless, so there’s no coordination needed between instances on different hosts. One thing worth planning for early: log volume grows faster than people expect once every container is shipping unfiltered stdout/stderr, so set up index/retention policies on your Elasticsearch (or equivalent) cluster from day one rather than after a disk-full incident. If you’re on Kubernetes rather than plain Docker hosts, Logspout isn’t the typical choice — Fluent Bit or Vector as a DaemonSet is more common there, since they integrate more natively with the Kubernetes API for pod metadata enrichment. Logspout shines specifically in plain Docker/Compose/Swarm environments where you don’t already have a Kubernetes-native logging pipeline.

Summary

Logspout earns its place in a Docker logging stack by doing one thing well: dynamically discovering containers and routing their log streams without requiring per-container configuration. It’s not a replacement for a full observability stack — you still need something to store, index, and query logs (ELK, Loki, a SaaS platform) — but as the collection layer, it removes the operational burden of manually wiring log shipping into every container you run.

References

Total
2
Shares

Leave a Reply

Previous Post
How to Get the Logs of a Container with docker logs

How to Get the Logs of a Container with Docker Logs: Complete Logging and Troubleshooting Guide

Next Post
How to Build an S3-Compatible Object Store with Cassandra on Kubernetes

How to Build an S3-Compatible Object Store with Cassandra on Kubernetes: Complete Guide

Related Posts