Dash0 Raises $110M Series B at $1B Valuation

Last updated: June 29, 2026

Dozzle: How to View Docker Container Logs in Real Time

Docker makes it easy to start services, but not always easy to follow what they are doing. The built-in docker logs command works well for a single container, especially when you already know where to look. It becomes less convenient when you want to compare several services, filter noisy output, share access with a teammate, or keep an eye on logs without staying attached to a terminal.

Dozzle solves that narrow problem with a focused web interface for live container logs. It streams stdout and stderr from your containers into the browser, gives you search and filtering controls, and lets you inspect logs across a running Docker host without building a full logging platform first.

This guide walks through what it takes to make Dozzle useful in practice: installation with Docker Compose, the main configuration options, authentication and security, multi-host monitoring with agents, SQL log queries through DuckDB, and the point where a real-time log viewer stops being enough.

It also covers the line where Dozzle stops being the right fit. Dozzle is a real-time viewer, not a long-term log management or observability platform, and that distinction matters once you need retention, correlation, alerting, or analysis over time.

For broader context on how Docker collects and exposes container logs, start with our docker logs guide first.

Prerequisites

You need a recent version of Docker Engine and Docker Compose installed on your machine to follow along. You'll also want at least one running container that's producing logs so you have something to look at once Dozzle is up and running.

What is Dozzle?

Dozzle is a stateless, browser-based log tail for Docker containers. It reads from the Docker API, the same source that powers docker logs, and streams that output to a web UI.

If a container crashes, Dozzle shows you whatever Docker still has in its log buffer (controlled by your logging driver's --log-opt max-size setting). It doesn't maintain its own history on top of that, so searching last week's logs or building dashboards over time isn't possible. The upside is that the image is about 7 MB compressed, it barely touches CPU or memory, and there's nothing to configure before logs start showing up.

Dozzle fits well in a few specific scenarios. For local development, it's great for watching logs from your Compose stack in one tab while you work. In staging and CI environments, it gives you a quick visual check of what's happening during deploys or test runs. For homelab and self-hosted setups, it lets you monitor a handful of services without any infrastructure overhead.

It doesn't fit where you need historical search beyond what Docker can retain on disk, log retention for compliance, or team-scale access controls. I'll cover what to reach for when you outgrow it by the end of this guide.

Dozzle and log driver compatibility

Dozzle reads logs through the Docker API, the same endpoint behind docker logs. On any Docker Engine from 20.10 onward, this works with every logging driver out of the box.

If you're shipping logs to a remote destination via splunk, fluentd, or awslogs, Docker automatically caches a local copy using its dual logging feature, and Dozzle reads from that cache.

The cache defaults to 5 rotated files of 20 MB each per container (before compression), which is plenty for live tailing. You can tune those limits with the cache-max-size and cache-max-file options in your daemon configuration.

If Dozzle shows an empty log stream for a container that's definitely producing output, check the container's log config:

bash
123
docker inspect \
--format='{{json .HostConfig.LogConfig}}' \
<container_name>

If the Type is json-file, local, or journald, those drivers support docker logs natively and Dozzle will always work. You'll typically see something like {"Type":"json-file","Config":{}}, which is fine.

If the Type is a remote driver like splunk, fluentd, or awslogs, look for cache-disabled in the Config object. When it's absent or "false", the dual logging cache is active and Dozzle works normally.

If it's "true", that's what's blocking Dozzle. Check /etc/docker/daemon.json too, since it can set cache-disabled as a daemon-wide default. Our Mastering Docker Logs guide has the full breakdown of driver behavior and cache configuration.

Installing and running Dozzle

The fastest way to get Dozzle running is a single docker run command:

bash
123456
docker run -d \
--name dozzle \
--volume=/var/run/docker.sock:/var/run/docker.sock:ro \
-p 8080:8080 \
amir20/dozzle:latest \
--no-analytics

A few things worth noting about those flags:

  • The --volume flag mounts the Docker socket into the container so Dozzle can talk to the Docker API. The :ro suffix makes it a read-only mount, which is a basic security measure.
  • The -p 8080:8080 maps port 8080 on your host to port 8080 inside the container (Dozzle's default).
  • Finally, --no-analytics disables the anonymous usage analytics that Dozzle collects by default via Google Analytics.

Confirm it's running:

bash
1
docker ps --filter name=dozzle

You should see output showing the dozzle container in a running state with port 8080 mapped:

text
12
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d9d1dcf07f96 amir20/dozzle:latest "/dozzle --no-analyt…" About a minute ago Up About a minute 0.0.0.0:8080->8080/tcp, [::]:8080->8080/tcp dozzle

For anything beyond a quick test, use a Compose file to run it alongside your services. Here's a complete setup with sensible defaults:

yaml
1234567891011121314
services:
dozzle:
image: amir20/dozzle:latest
container_name: dozzle
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
ports:
- "8080:8080"
environment:
DOZZLE_NO_ANALYTICS: "true"
DOZZLE_LEVEL: info
DOZZLE_TAILSIZE: 300
DOZZLE_FILTER: "status=running"
  • DOZZLE_LEVEL controls Dozzle's own log verbosity, not the logs it displays.
  • DOZZLE_TAILSIZE sets how many lines Dozzle fetches when you first open a container's log stream (300 is a good balance between context and load time).
  • DOZZLE_FILTER set to status=running prevents stopped containers from cluttering the sidebar, which gets noisy fast on hosts with containers that start and stop frequently.

Once configured, start it with:

bash
1
docker compose up -d

To verify that Dozzle is up and running, open http://localhost:8080 in your browser. You should see the Dozzle interface with a sidebar listing your running containers:

Dozzle lists running containers

Click on any container on the list to see its logs streaming in real time:

Dozzle showing container logs

Rather than walking through every button, here are the workflows that matter most.

The container sidebar on the left lists all running containers, filterable by name. If you're using Docker Compose, containers from the same project automatically group together under their stack name, which keeps things organized when you're running multiple Compose stacks on one host.

Dozzle makes it easy to spot errors

The log stream itself auto-scrolls as new lines come in. Dozzle auto-detects JSON-formatted logs and pretty-prints them with syntax highlighting. If your logs include a level field (like most structured logging libraries produce), Dozzle color-codes entries by severity. This makes it easy to spot errors without reading every line.

Dozzle lets you tweak log levels

If you're actively debugging an issue and only care about errors and warnings, you can hide the info and debug noise with a click rather than scanning through everything visually.

Dozzle supports split view

Split view lets you watch two or more containers side-by-side. When you observe a 500 error and you need to see what happened in the API server, the database, and the cache layer at the same moment, you can open all three in split panes and scroll to the same timestamp.

The search bar filters containers by name, which helps when you're running dozens of them and need to find a specific service quickly. There's no built-in search across log content itself, so if you need to query logs by text, the SQL feature covered later in this guide is the way to go.

Dozzle shows lightweight CPU, memory and network charts

Each container view also shows lightweight CPU, memory, and network charts. These aren't a replacement for proper monitoring, but they're useful for a quick check when you suspect a container is consuming more resources than it should. Our docker stats guide goes deeper on container resource monitoring.

Setting up authentication and access control in Dozzle

If you're only using Dozzle on your local machine for development, you can skip this section for now. But if Dozzle is accessible over a network, even an internal one, read this before doing anything else.

The Docker socket (/var/run/docker.sock) gives any container that mounts it what amounts to root access on the host. Even with the :ro flag, the Docker API still exposes container listings, log content, image details, and network configuration. An exposed, unauthenticated Dozzle instance is a reconnaissance goldmine for anyone who finds it, so always put Dozzle behind authentication if it's reachable from the public internet.

The simplest approach is Dozzle's built-in file-based authentication. Generate a credentials file using Dozzle's generate command:

bash
1234
docker run -it --rm amir20/dozzle generate admin \
--password <your-secure-password> \
--email admin@example.com \
--name "Admin" > users.yml

This produces a users.yml file with a bcrypt-hashed password:

yaml
12345678
# users.yml
users:
admin:
email: admin@example.com
name: Admin
password: $2a$11$FIpatJvtzq/z8Rm/kQeGBOFk0DFQlMycobbtfRg0Ad/7CrNXHLqBm
filter: ""
roles: ""

Then mount the directory containing that file and enable the simple auth provider:

yaml
123456789101112
services:
dozzle:
image: amir20/dozzle:latest
container_name: dozzle
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./data:/data
ports:
- "8080:8080"
environment:
DOZZLE_AUTH_PROVIDER: simple

Place your users.yml inside the ./data directory. Dozzle also writes user settings and notification state to /data, so mounting the directory (rather than just the file) lets those persist across restarts. After bringing the stack up, you'll see a login screen.

Dozzle login screen

One thing to watch out for is that many older tutorials still reference DOZZLE_USERNAME and DOZZLE_PASSWORD environment variables for single-user auth. That approach was deprecated in v5 and removed in v6. If you try to use those variables with a current version of Dozzle, it won't start.

The users.yml file also supports per-user filter and roles fields. Filters restrict which containers a user can see (using Docker label syntax like label=com.example.app), and roles control whether a user can access shell or container actions. See Dozzle's authentication docs for the full reference.

For production deployments, Dozzle also supports forward proxy authentication with tools like Authelia, Authentik, or Cloudflare Access. This is the better option when you need centralized auth with multi-factor authentication. Dozzle reads user identity from headers like X-Forwarded-User and X-Forwarded-Email set by the proxy.

If you're putting Dozzle behind a reverse proxy (and you should for any non-local deployment), make sure WebSocket connections are handled properly. Dozzle uses WebSockets for its live log stream, and without the right proxy headers the stream fails silently. Here's a minimal Nginx config:

nginx
12345678910111213141516
server {
listen 443 ssl;
server_name logs.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 86400s;
}
}

The Upgrade and Connection headers enable WebSocket passthrough. The proxy_read_timeout prevents Nginx from closing long-lived connections. See Dozzle's reverse proxy guide for Traefik and Caddy examples.

One last thing: Dozzle can start, stop, and restart containers from the UI (via DOZZLE_ENABLE_ACTIONS) and open a shell into running containers (via DOZZLE_ENABLE_SHELL). Both are off by default. If you enable either, authentication is non-negotiable as these give the UI the same power as docker stop or docker exec on the command line.

Monitoring multiple Docker hosts with Dozzle agents

If you run containers across more than one machine, Dozzle's agent mode lets you view logs from all of them in one place. You deploy a lightweight agent on each remote host, then point your central Dozzle instance at those agents. Communication happens over gRPC on port 7007, secured with TLS by default.

On each remote machine, deploy Dozzle with the agent subcommand:

yaml
12345678910
# docker-compose.yml on the remote host
services:
dozzle-agent:
image: amir20/dozzle:latest
command: agent
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
ports:
- "7007:7007"

On your central host, point Dozzle at the agents using DOZZLE_REMOTE_AGENT. The connection string format is endpoint|name|group, where name and group are optional:

yaml
123456789101112131415
# docker-compose.yml on the central host
services:
dozzle:
image: amir20/dozzle:latest
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./data:/data
ports:
- "8080:8080"
environment:
DOZZLE_REMOTE_AGENT: >-
10.0.1.10:7007|web-1|Production, 10.0.1.11:7007|web-2|Production
DOZZLE_AUTH_PROVIDER: simple
DOZZLE_NO_ANALYTICS: "true"

The groups show up as collapsible sections in the sidebar, which is useful once you're managing more than a few hosts. If you only want to see containers from remote agents and not from the central host itself, you can skip the docker.sock mount on the central Compose file.

You don't need to expose port 7007 publicly. If the agent and hub are on the same Docker network, the agent is reachable by container name without any port mapping. For hosts on separate networks, restrict port 7007 with firewall rules so only the central Dozzle host can reach it. See the Dozzle agent documentation for the full reference.

Querying logs with Dozzle's SQL engine

Dozzle includes a SQL engine powered by DuckDB compiled to WebAssembly (introduced in v8.5, still labeled beta). It runs entirely in your browser, so your data never leaves your machine and there's no server-side processing. Dozzle reads the loaded JSON logs, creates a virtual logs table in DuckDB, and you query against that.

The key requirement: this only works with JSON-structured logs. If your containers output plain text, the SQL engine won't have structured fields to query. Open it from the dropdown menu ("SQL Analytics") or with Ctrl+Shift+F (Cmd+Shift+F on macOS).

Because Dozzle unnests JSON fields into columns, you can query structured fields directly rather than doing text matching. A few practical examples:

Filter by log level and status code:

sql
12345
SELECT time, level, message
FROM logs
WHERE level = 'error'
ORDER BY time DESC
LIMIT 50;

Count logs grouped by level:

sql
123
SELECT level, COUNT(*) AS count
FROM logs
GROUP BY level;

Aggregate errors by minute to spot spikes:

sql
1234567
SELECT
date_trunc('minute', timestamp) AS minute,
COUNT(*) AS error_count
FROM logs
WHERE level = 'error'
GROUP BY minute
ORDER BY minute DESC;

Query nested JSON fields (if your logs contain objects):

sql
12345
SELECT message.path, message.status,
message.duration
FROM logs
WHERE message.status >= 400
ORDER BY message.duration DESC;

You're querying what's loaded in the browser, not Docker's full log history, so this is best suited for ad-hoc debugging rather than historical analysis.

Setting up Dozzle alerts with webhooks

Dozzle v10 introduced webhook-based alerts, and subsequent releases added metric and container event triggers on top of the original log pattern matching.

Dozzle notification destination

The setup starts on the Notifications page. You first add a destination, which is a webhook URL that receives an HTTP POST when an alert fires. Dozzle ships with built-in payload templates for Slack, Discord, and ntfy, and you can write custom templates using Go's text/template syntax for anything else.

Dozzle alerts

Each alert combines a container expression (filtering by name, label, or image) with a trigger. Log triggers fire when a line matches your pattern, while metric triggers fire when CPU or memory exceeds a threshold over a smoothed rolling average, with configurable sample windows and cooldown periods to avoid false alarms from short spikes. Event triggers fire on Docker lifecycle events like die, oom, or health_status. Common exit codes like 0, 130 (SIGINT), 143 (SIGTERM), and 137 (SIGKILL) are excluded by default so you don't get alerts every time a container stops normally.

Dozzle alerts work well for catching crashes and threshold breaches in staging or homelab environments. They don't have escalation policies or SLO-based thresholds, so production alerting still needs a dedicated platform.

When Dozzle stops being enough

Dozzle is great when you need to look at container logs in the moment. It gives you a fast, simple way to see what a container is saying right now, without setting up a larger logging or monitoring stack.

The moment you need to understand what happened before you opened the Dozzle UI, or how one log line relates to the rest of your system, live log viewing usually isn't enough. You need observability. The difference becomes clear as soon as production asks harder questions:

  • Which deploy introduced this spike in 500 responses?
  • Did the error rate stay above 5% last night, or was it a brief burst?
  • Which service handled this request before it timed out?
  • What were the CPU, memory, and database metrics when the error happened?
  • Can you search and audit the relevant logs months from now?

Dozzle isn't designed to answer those, and that's not a flaw. It's a focused real-time log viewer. For production operations, you need a system that keeps context over time and connects the telemetry signals.

That's where OpenTelemetry fits in. It gives you a vendor-neutral way to collect logs, metrics, and traces from your applications and infrastructure. Instead of treating container logs as isolated lines of text, you send them through the same telemetry pipeline as the rest of your system.

Dash0 is an observability platform built around OpenTelemetry from the ground up. It stores telemetry as OTLP data directly, without proprietary agents or vendor-specific translation. Dash0 supports PromQL and SQL for querying, and Perses-based dashboards for visualization, so your telemetry pipeline stays aligned with the broader cloud native ecosystem rather than locked into a single vendor's conventions.

Dash0 homepage

Once the data is in Dash0, you can start from a log entry, move to the trace behind the request, and inspect the surrounding infrastructure metrics without switching tools. Instead of asking someone to reproduce an issue while you watch Dozzle, you investigate what already happened.

Dash0 log-trace correlation

For Kubernetes environments, the Dash0 Kubernetes Operator handles instrumentation and collection with minimal manual work. For standalone Docker hosts, the Docker logs with OpenTelemetry guide shows how to move container stdout into an OpenTelemetry pipeline and send it to Dash0.

Authors
Ayooluwa Isaiah
Ayooluwa Isaiah