How to ingest Docker logs into Loki

Ingesting Docker logs into Loki sends container stdout and stderr to a queryable log backend without changing application code. The Loki Docker driver attaches to Docker Engine as a logging plugin and forwards selected container logs to a Loki push endpoint.

The plugin is installed on each Docker host that runs containers whose logs should be shipped. The Loki URL must be reachable from the Docker host, not from inside the application container, and plugin installation changes host Docker state.

Use this path for Docker Engine hosts where a logging driver is acceptable. For Kubernetes, systemd journals, files, or richer parsing pipelines, use Grafana Alloy or another log collector instead of the Docker logging driver.

Steps to ingest Docker logs into Loki:

  1. Confirm Loki is reachable from the Docker host.
    $ curl --silent http://127.0.0.1:3100/ready
    ready
  2. Install the Loki Docker driver plugin.
    $ docker plugin install grafana/loki-docker-driver:3.7.0-amd64 \
      --alias loki \
      --grant-all-permissions
    Plugin "grafana/loki-docker-driver:3.7.0-amd64" is requesting the following privileges:
     - network: [host]
     - mount: [/var/log]
    ##### snipped #####
    Installed plugin grafana/loki-docker-driver:3.7.0-amd64

    Docker plugins are host-level Docker Engine extensions. Install them only on hosts where plugin privileges and lifecycle are approved.

  3. Confirm the plugin is enabled.
    $ docker plugin ls
    ID                  NAME   DESCRIPTION           ENABLED
    4b921d7f4c2a        loki   Loki Logging Driver   true
  4. Start a test container with the Loki logging driver.
    $ docker run --rm \
      --log-driver=loki \
      --log-opt loki-url=http://127.0.0.1:3100/loki/api/v1/push \
      --log-opt loki-external-labels=job=docker-smoke,service_name=checkout-api \
      alpine:3.22 \
      sh -c 'echo "checkout complete trace_id=5b8efff798038103d269b633813fc700"'
    checkout complete trace_id=5b8efff798038103d269b633813fc700

    The label set should stay small and stable. Put request IDs and trace IDs in the log line or structured metadata instead of labels.

  5. Query Loki for the test container log.
    $ curl --silent --get http://127.0.0.1:3100/loki/api/v1/query_range \
      --data-urlencode 'query={service_name="checkout-api"} |= "checkout complete"' \
      --data-urlencode limit=5
    {
      "status": "success",
      "data": {
        "resultType": "streams",
        "result": [
          {
            "stream": {
              "job": "docker-smoke",
              "service_name": "checkout-api"
            },
            "values": [
              [
                "1782008380038503000",
                "checkout complete trace_id=5b8efff798..."
              ]
            ]
          }
        ]
      }
    }
  6. Configure the logging driver for a Compose service when the test query works.
    compose.yaml
    services:
      checkout:
        image: alpine:3.22
        command: sh -c 'while true; do echo "checkout heartbeat"; sleep 30; done'
        logging:
          driver: loki
          options:
            loki-url: http://127.0.0.1:3100/loki/api/v1/push
            loki-external-labels: job=checkout,service_name=checkout-api
  7. Start the Compose service.
    $ docker compose up -d checkout
    Container app-checkout-1  Started
  8. Query the Compose service labels in Loki.
    $ curl --silent --get http://127.0.0.1:3100/loki/api/v1/query_range \
      --data-urlencode 'query={job="checkout",service_name="checkout-api"}' \
      --data-urlencode limit=5
    {
      "status": "success",
      "data": {
        "resultType": "streams",
        "result": [
          {
            "stream": {
              "job": "checkout",
              "service_name": "checkout-api"
            },
            "values": [
              [
                "1782008400000000000",
                "checkout heartbeat"
              ]
            ]
          }
        ]
      }
    }
  9. Remove the plugin only when no container still uses it.
    $ docker plugin disable loki --force
    loki
    $ docker plugin rm loki
    loki

    Disabling the plugin can interrupt log shipping for containers configured to use the loki logging driver.