ZYVOPMulti-Platform Sync
SeriesAI NewsWhy ZyVOPJoin Discord
LoginGet Started
ZYVOP
The Developer Publishing Hub
PrivacyTermsGuidelinesDMCACommunity
© 2026 ZyVOP
HomeTutorialDozzle: The Complete Guide to Real-Time Docker Log Viewing
Tutorial

Dozzle: The Complete Guide to Real-Time Docker Log Viewing

How to set up Dozzle for live Docker logs in your browser, then search them, get alerts, watch multiple hosts, and lock it down properly.

Samod Alex
Samod Alex
September 21, 2026•
12 min read
Dozzle: The Complete Guide to Real-Time Docker Log Viewing
#self-hosting#Docker#Log Monitoring#Dozzle#DevOps

Written against Dozzle v11.1.x, September 2026. v11 shipped on September 11, so expect details to keep moving.

docker logs -f is fine for one container. Then you end up with a Compose stack of eight services, or three hosts, and a bug that only shows up when the API, the worker, and the database are all unhappy at the same moment. Now you're flipping between terminal tabs, trying to line up timestamps by eye.

Dozzle solves that one problem. It's a small web app that shows container logs live in your browser: open the page, click a container, watch the lines arrive. This post goes from the two-minute install through search, alerts, multiple hosts, Kubernetes, and locking it down. It's based on v11.

What it is, and what it isn't

Dozzle is a live tail, nothing more. It doesn't store logs. It reads from the Docker API, the same place docker logs reads from, so what you see is whatever Docker still holds, and how much that is depends on your logging driver's rotation settings. Once Docker drops a line, Dozzle can't show it.

Keep in mind: Dozzle is a live viewer, not a log store. If you need history, it has to come from Docker's log settings or a separate logging stack.

The upside of being that simple is that the image is only a few megabytes compressed and there's next to nothing to configure before logs appear. It works with Docker, Swarm, and Kubernetes, and with Colima and Podman too. Podman needs its remote socket enabled first.

The limits are worth knowing up front. The project says it's been tested with hundreds of containers, but it has no offline searching, and it points people who need full search toward tools like Loggly, Papertrail, or Kibana. Dozzle is for watching what's happening right now, not for digging through last week.

What changed in v10 and v11

A few things worth knowing if you last used Dozzle a while ago:

  • v10 introduced alerts with webhook delivery. Today they cover logs, resource metrics, and container events.

  • v11 is the biggest visual overhaul so far: flat, neutral panels, with color saved for things that need your attention. It also brought GitHub and OIDC sign-in, recognition of more log formats, and alerts that persist across reloads.

  • v11.1 added a separate oidc auth provider that reads users and roles from the token, a login-first setup wizard for fresh installs, and generate-certs for giving agents their own certificate.

One upgrade catch: session tokens are now signed with a random secret kept in the data directory, so everyone gets signed out once after upgrading.

Quick start

The one-liner:

docker run -d --name dozzle \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  -v dozzle_data:/data \
  -p 8080:8080 \
  amir20/dozzle:latest

Open http://localhost:8080 and your containers should be listed. For something you plan to keep running, a Compose file is easier to maintain:

services:
  dozzle:
    image: amir20/dozzle:latest   # pin a specific version tag in production
    container_name: dozzle
    restart: unless-stopped
    ports:
      - "8080:8080"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./dozzle-data:/data
    environment:
      DOZZLE_NO_ANALYTICS: "true"

Some notes on that file:

  • Mount /data. Alert and destination settings are stored there, so without a volume they vanish on restart. User settings and your users.yml live there too.

  • Dozzle sends anonymous usage analytics by default. DOZZLE_NO_ANALYTICS turns that off.

  • Pin the image tag. With Dozzle moving fast (v11 signed everyone out on upgrade), latest can bite you at a bad time.

Two habits worth having from day one: mount /data so your settings survive restarts, and pin the image tag instead of using latest.

Getting around the interface

The sidebar lists your containers and groups Compose services by stack name automatically. v11 rebuilt it around collapsible groups with counts, and each container's icon carries a status badge. Container names are fuzzy-searchable, so on a busy host you type a few letters and jump straight to the service.

Logs stream in the main pane. Dozzle detects JSON logs and pretty-prints them, and if your entries have a level field they're colored by severity. In v11, warn and error rows get a light tint so they stand out as you scroll, and a live indicator plus a floating scroll readout show where you are in the container's lifetime. If you only care about problems, one click hides the info and debug lines.

Split view is the feature that actually replaces terminal tabs. It puts several containers side by side, so when the API returns a 500 you can watch the database and cache logs at the same timestamp. In v11 the pinned columns are stored in the URL, which means a side-by-side view is just a link you can send to a teammate.

Each container also gets small CPU and memory charts. They're basic, but enough to tell whether a container is struggling.

Searching and querying logs

For quick filtering there's regex search over the logs. For anything more analytical there's a SQL engine.

The SQL engine runs DuckDB compiled to WebAssembly inside your browser, so your logs never leave your machine. Dozzle loads your JSON logs into a virtual logs table that you can query. You open it from the menu or with Ctrl/Cmd+Shift+F, and it only works on JSON-structured logs. The docs still label it beta.

It queries what's already loaded in the browser, not Docker's full history. That makes it good for ad-hoc debugging, but don't expect trend analysis from it. WebAssembly caps it at 4 GB of memory, and if you run out you refresh the page.

-- How noisy is each severity right now?
SELECT level, COUNT(*) AS n
FROM logs
GROUP BY level;

-- Slowest failing requests (field names depend on your JSON logs)
SELECT message.path, message.status, message.duration
FROM logs
WHERE message.status >= 500
ORDER BY message.duration DESC
LIMIT 20;

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

If you already emit structured logs, this can replace a lot of docker logs | jq | grep pipelines.

Grouping and naming containers

Dozzle groups by stack by default. To make your own groups, add the dev.dozzle.group label, and containers that share a group name end up together in the UI. There's also a dev.dozzle.name label if you want a friendlier display name.

services:
  api:
    image: myorg/api:1.4.2
    labels:
      dev.dozzle.group: shop
      dev.dozzle.name: shop-api

Under Swarm, if Dozzle sees the service-name label, it switches to a swarm view that joins all tasks of the same service.

Limiting what Dozzle can see

DOZZLE_FILTER restricts which containers Dozzle can see at all. Filters are passed straight to Docker, in the same style as docker ps --filter, so DOZZLE_FILTER=label=color shows only containers that carry that label. They can also be set per agent and per user, and they stack: a container has to match all of them to show up.

Be careful with filters that exclude stopped containers, like status=running. The container that just crashed is often the one you need to read, and a filter like that hides it completely.

Security

Mounting the Docker socket gives a container effectively root-level access to the host, and the :ro in the examples above doesn't change that. It only marks the socket file read-only on disk, so API calls still pass through and create, delete, and update operations stay possible. If you don't need actions, put a socket proxy such as tecnativa/docker-socket-proxy between Dozzle and the daemon to limit what it can do.

An unauthenticated Dozzle on a reachable network also shows every container's logs to anyone who finds it, and logs often contain tokens and personal data.

Rule of thumb: no authentication, no exposure beyond localhost.

Built-in auth

Start by generating a users file:

docker run -it --rm amir20/dozzle generate admin \
  --password 'change-me' \
  --email [email protected] \
  --name "Admin" > users.yml

Put users.yml in your mounted /data directory and set DOZZLE_AUTH_PROVIDER: simple. Passwords are stored bcrypt-hashed. Each user can also have a filter, which restricts which containers they can see by label, and roles, which control what they can do: shell, actions, download, notifications, and cloud. A user with no roles listed gets all of them, so set roles explicitly for anyone who shouldn't have full access. The instance-wide flags for shell and actions still have to be on before those roles do anything.

GitHub and OIDC (v11)

v11 lets you sign in with GitHub or any OIDC provider, such as Authentik, Keycloak, Pocket ID, or Google. It sits on top of the simple provider, so users.yml stays the allowlist, no accounts are created automatically, and password login keeps working. If you'd rather manage users and roles in your identity provider, v11.1 added a separate oidc provider that reads them from the token.

environment:
  DOZZLE_AUTH_PROVIDER: simple
  DOZZLE_AUTH_GITHUB_CLIENT_ID: <your-client-id>
  DOZZLE_AUTH_GITHUB_CLIENT_SECRET: <your-client-secret>

Forward-proxy auth

In production, Dozzle can trust identity headers from a proxy like Authelia, Authentik, or Cloudflare Access. That's the better route if you want centralized multi-factor auth, but it comes with one hard rule: Dozzle believes the Remote-User header on every request. Publish only the proxy and keep Dozzle on an internal network (expose, not ports), because anyone who can reach Dozzle directly can set that header and log in as whoever they like. Also map roles from your proxy, for example DOZZLE_AUTH_HEADER_ROLES: Remote-Groups for Authelia groups, since without a mapping every authenticated user gets all roles.

Actions and shell are opt-in

Container start/stop/restart actions (DOZZLE_ENABLE_ACTIONS) and shell access (DOZZLE_ENABLE_SHELL) are off by default. If you turn either on, get authentication in place first. They give the web UI the same power as docker stop and docker exec.

Reverse proxy

Dozzle streams logs over Server-Sent Events and uses WebSockets for shell and attach. That gives a reverse proxy three jobs: don't buffer responses, forward the WebSocket upgrade headers, and don't compress text/event-stream. Buffering makes logs arrive in bursts or not at all. A minimal nginx location:

location / {
    proxy_pass http://127.0.0.1:8080;

    chunked_transfer_encoding off;
    proxy_buffering off;
    proxy_cache off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 3600s;
}

The long read timeout matters too, because logs stop after a few seconds when the proxy's timeouts are short. Behind Traefik, the default compress middleware breaks SSE, so exclude text/event-stream. In Caddy, flush_interval -1 turns off response buffering. And if you mount Dozzle under a sub-path with DOZZLE_BASE, make sure the proxy passes the full path through instead of stripping the prefix.

Proxy tip: if logs arrive in bursts or not at all, response buffering is the first thing to turn off.

Keep it updated

Dozzle's security page lists several advisories from 2026, including these high-severity ones:

  • an unauthenticated SSRF through the webhook test endpoint on default deployments without auth

  • cross-site WebSocket hijacking on the exec and attach endpoints, which got around authentication for setups with shell enabled (versions up to 10.5.1)

  • a label-based access bypass in the agent that allowed unauthorized shell access

So: turn on auth, keep the container patched, and keep it off the open internet.

Monitoring multiple hosts with agents

To see several machines in one UI, run Dozzle in agent mode on each remote host and point a central instance (the hub) at them. Agents listen on port 7007, and the hub connects to them over TLS.

# On each 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"   # keep this on a private network
# On the central host
services:
  dozzle:
    image: amir20/dozzle:latest
    volumes:
      - ./data:/data
    ports:
      - "8080:8080"
    environment:
      DOZZLE_AUTH_PROVIDER: simple   # expects users.yml in ./data
      DOZZLE_REMOTE_AGENT: "10.0.1.10:7007|web-1|production,10.0.1.11:7007|web-2|production"

The connection string looks like endpoint|name|group. All three parts are optional, and groups show up as collapsible sections in the sidebar, each with a button that merges the group's logs into one view. If the hub only needs to show remote hosts, you can skip mounting the local socket there. If you run Swarm, you don't need agents at all, because Dozzle discovers the cluster on its own.

Treat the agent port as sensitive. The TLS certificate Dozzle ships with is identical in every copy of the image, so it encrypts the connection but doesn't prove who is on the other end. Anyone who can reach port 7007 can connect their own Dozzle to your agent, read every log on that host, and run commands inside its containers. The agent also ignores DOZZLE_ENABLE_SHELL and DOZZLE_ENABLE_ACTIONS, because those flags only control what the UI offers. Keep 7007 on a private network (on a shared Docker network you don't need to publish it at all), and if anything you don't control can reach it, generate your own certificate with generate-certs so agents only accept your hub.

Important: anyone who can reach port 7007 can read every log and run commands inside that host's containers. Keep it on a private network.

Alerts

Since v10, Dozzle can tell you when something breaks instead of waiting for you to notice. It watches logs, resource metrics, and lifecycle events, evaluates your rules on your own instance, and sends notifications to a webhook, Slack, Discord, or ntfy.

Each alert has a container expression, which decides which containers to watch, and a trigger expression. Triggers come in three types: log, metric, and event. Setup lives on the Notifications page: add a destination first, then create rules. Webhook destinations come with built-in Slack, Discord, and ntfy payloads, and you can write custom Go text/template payloads for anything else. There's a Test button, so you can confirm delivery before saving.

Some example rules, written in the expression style the docs use:

# 5xx responses from production APIs
Container: name contains "api" && labels["env"] == "production"
Log:       message.status >= 500

# Memory pressure on the database
Container: name == "postgres"
Metric:    memory > 85

# Any OOM kill, anywhere
Container: true
Event:     name == "oom"

Metric alerts evaluate a smoothed average over a sample window and have a cooldown between triggers, so a brief spike doesn't flood your channel. For die events, the docs' example excludes exit codes 0, 130, 143, and 137, since those show up on routine stops and update cycles.

Dozzle Cloud is optional. Your rules always live on your self-hosted instance, but if you link it, delivery features such as grouping repeated failures, summaries, muting, and mobile channels are configured there.

Alerts are deliberately simple. There are no escalation policies or on-call rotations, so treat them as a safety net for staging and homelabs, not as a production pager.

Kubernetes

For Kubernetes, run Dozzle with DOZZLE_MODE=k8s. The docs include a full RBAC manifest; at minimum it needs read access to pods, pod logs, and nodes. Logs work without the Kubernetes Metrics API (metrics-server), but CPU and memory stay empty without it. Give it a persistent volume for /data so your alert config survives restarts.

env:
  - name: DOZZLE_MODE
    value: "k8s"
  - name: DOZZLE_NAMESPACE
    value: "prod,staging"   # optional; defaults to all namespaces
  - name: DOZZLE_FILTER
    value: "env=prod"       # optional label filter

The docs still call Kubernetes support a newer feature that may have limitations compared to the Docker version, and the release notes bear that out. v11.1.1 alone includes Kubernetes hardening, alerts for CronJob pods, and fixes for duplicate ReplicaSets and finished Jobs. If you run Dozzle on Kubernetes, keep it up to date.

Letting AI assistants read your logs (MCP)

Dozzle can expose an MCP endpoint so coding assistants can inspect your containers. It's disabled by default. Enable it with DOZZLE_ENABLE_MCP=true and it's served at /api/mcp from the same container. Every tool is read-only: listing containers and hosts, fetching and searching logs, and pulling CPU and memory history.

One warning: with no auth provider configured, the endpoint is publicly accessible, so set up authentication first. Once auth is on, MCP clients have to present credentials too.

When your app logs to files instead of stdout

Dozzle only sees what Docker captures, which means stdout and stderr, exactly like docker logs. Files inside a container are invisible to it.

The best fix is to log to the console, or symlink the log file to /dev/stdout, as the official nginx image does. If you can't, the docs suggest a small sidecar that tails the file:

docker run -d --name app-log --network none \
  --label dev.dozzle.name=app-log \
  --log-opt max-size=10m --log-opt max-file=3 \
  -v /var/log/myapp:/logs:ro \
  alpine tail -n 1000 -F /logs/app.log

Use -F instead of -f so the tail reopens the path after log rotation. Mount the directory, not the single file, because a single-file bind mount stays attached to the old inode.

Troubleshooting

  • Empty stream for a container that's clearly running: if it uses a remote logging driver such as splunk, fluentd, or awslogs, check whether cache-disabled is set to true (and look at daemon.json too). That setting blocks the local cache Dozzle reads from.

  • Logs arrive in bursts, or stop after a few seconds, behind a proxy: response buffering is on, text/event-stream is being compressed, or the read timeout is too short. See the reverse proxy section.

  • Shell disconnects immediately: the proxy isn't forwarding the WebSocket upgrade headers.

  • Won't start after following an old tutorial: DOZZLE_USERNAME and DOZZLE_PASSWORD are no longer supported. Use users.yml instead.

  • Alerts vanish after a restart: /data isn't mounted as a volume.

  • Signed out on every restart: if /data isn't writable, Dozzle falls back to an in-memory session secret (and warns about it), so sessions drop whenever it restarts.

  • Everyone logged out after upgrading to v11: expected, and it only happens once.

When to outgrow Dozzle

Dozzle answers "what is this container saying right now?" It can't answer which deploy introduced this spike, did the error rate stay high overnight, or what happened to this request across three services last week. Those need retention, correlation, and analysis over time, which a real-time viewer doesn't give you. When you reach that point, add a proper logging or observability stack, like Loki, an OpenTelemetry pipeline, or a hosted platform, and keep Dozzle for the quick look.

Checklist before you rely on it

  1. Pin the image version and update on a schedule.

  2. Mount /data as a persistent volume.

  3. Turn on authentication (users.yml, OIDC/GitHub, or a forward proxy) before exposing it beyond localhost.

  4. Leave actions and shell off unless you need them, and put a socket proxy in front of the Docker socket if you don't need actions.

  5. For multiple hosts, use agents instead of exposing a Docker socket, keep port 7007 on a private network, and generate your own agent certificate if it's reachable from anywhere else.

  6. Set log rotation (max-size, max-file) so there's enough history to look at.

  7. Log to stdout, in JSON if you can, so you get level coloring, SQL queries, and structured alerts.

References

  • Dozzle docs and What's New in v11

  • Alerts, Agent Mode, and Kubernetes

  • Reverse Proxy & Base Path, Simple authentication, and Filters

  • SQL Engine, MCP Integration, and Log Files on Disk

  • Container Groups and supported environment variables

  • Authentication, Forward Proxy, and Docker's dual logging docs

  • Security advisories and release notes

Comments (0)

Join the discussion by logging into your account.

Samod Alex
Samod Alex

Passionate developer sharing knowledge about modern web technologies and best practices.

Subscribe to Samod Alex's Newsletter

Direct email dispatches when new stories are published. Zero algorithms.

Like
Love
Clap
Fire
Party
Wow

More from Samod Alex

View profile

Introducing GPT-6 Sol and Luna: What OpenAI's Cheaper Tier Means for Builders

OpenAI cut GPT-6 Sol and Luna prices in half, improved caching for agent workloads, and published alignment numbers worth reading before granting an agent more autonomy. Here is what changed, what to verify yourself, and where OpenAI's own benchmark comparisons need a caveat.

6 minSep 23

What Is Impersonation Risk Detection? Inside Apple's Trust Insights Framework for iOS 27

Impersonation Risk Detection flags likely coercion in the moment, but each app decides how to respond. For developers, the real story is Trust Insights: a new iOS 27 Swift framework, its five operation categories, mandatory feedback, on-device privacy, and where it falls short.

7 minSep 20

macOS 27 Golden Gate: What Shipped, What's Dormant, and What's Missing

macOS 27 Golden Gate landed September 14 with a conversational Siri, a toned-down Liquid Glass, and the end of major macOS support for Intel Macs. Release-candidate research also points to dormant hooks for outside models.

14 minSep 19

Jensen Huang Says AI Doesn't Need New Regulation. Is He Right?

Nvidia's Jensen Huang argues AI safety is an engineering problem best left to companies, not lawmakers. Rivals like Dario Amodei and Sam Altman disagree, and recent incidents raise doubts about trusting the market alone.

4 minSep 16

Obama Urges Democrats to Have a 'Clear Plan' for AI Safeguards

Barack Obama is urging Democrats to develop a clear AI policy focused on safety, jobs, children, and responsible innovation as the party looks to define its position ahead of the 2026 midterms.

5 minSep 14