dozzle Documentation

Realtime log viewer for containers. Supports Docker, Swarm and K8s.

RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy dozzle on any server, homelab, or cloud container platform.

File: README.md

Dozzle - [dozzle.dev](https://dozzle.dev/)

Dozzle is a lightweight, web-based application for monitoring Docker logs in real time. It doesn't store any log filesβ€”it's designed purely for live log viewing.

https://github.com/user-attachments/assets/66a7b4b2-d6c9-4fca-ab04-aef6cd7c0c31



[!NOTE]
If you like Dozzle, check out dtop, a top-like application for monitoring Docker containers. It integrates with Dozzle to link directly to container logs.

Features

  • Intelligent fuzzy search for container names
  • Search logs using regex
  • Search logs using SQL queries
  • Small memory footprint
  • Split screen for viewing multiple logs
  • Live stats with memory and CPU usage
  • Multi-user authentication with support for forward proxy authorization
  • Swarm mode support
  • Agent mode for monitoring multiple Docker hosts
  • Dark mode

Dozzle has been tested with hundreds of containers. However, it doesn't support offline searching. Products like Loggly, Papertrail, or Kibana are better suited for full search capabilities.

Getting Started

Dozzle is a small container (7 MB compressed). Pull the latest release with:

text
$ docker pull amir20/dozzle:latest

Running Dozzle

The simplest way to use Dozzle is to run the Docker container. Mount the Docker Unix socket with --volume to /var/run/docker.sock:

text
$ docker run --name dozzle -d --volume=/var/run/docker.sock:/var/run/docker.sock -v dozzle_data:/data -p 8080:8080 amir20/dozzle:latest

Dozzle will be available at http://localhost:8080/.

Here is a Docker Compose example:

text
services:
  dozzle:
    container_name: dozzle
    image: amir20/dozzle:latest
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - dozzle_data:/data
    ports:
      - 8080:8080
volumes:
  dozzle_data:

For advanced options like authentication, remote hosts, or common questions, see the documentation at dozzle.dev.

Image Tags

Images are published to both Docker Hub and ghcr.io with identical tags.

Tag Description
latest Most recent release. Recommended for most users.
v10.6.15 An exact release. Pin this for reproducible deployments.
v10.6 Latest patch within that minor version. Picks up bug fixes only.
v10 Latest release within that major version. Picks up new features too.
alpine Same as latest, but on an Alpine base instead of scratch.
v10.6.15-alpine, v10.6-alpine, v10-alpine Alpine variants of the version tags above.
master Built from the master branch on every push. Unreleased and unstable.
pr-1234, pr-1234-alpine Built from pull request #1234, for testing a fix before it ships.

The default images are built FROM scratch and contain only the Dozzle binary, which is why they are around 7 MB compressed. That also means there is no shell inside them. Use the alpine variants only if something in your setup needs one, most commonly platforms that bind-mount a #!/bin/sh wrapper over the container entrypoint such as Unraid's per-container Tailscale toggle. See the FAQ for details.

Avoid latest and master in production. latest moves on every release and master is unreleased code.

Swarm Mode

Dozzle works with Docker Swarm. You can run Dozzle as a global service:

text
$ docker service create --name dozzle --env DOZZLE_MODE=swarm --mode global --mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock -p 8080:8080 amir20/dozzle:latest

See the Swarm Mode documentation for more details.

Agent Mode

Dozzle can monitor multiple Docker hosts. Run Dozzle in agent mode with:

text
$ docker run -v /var/run/docker.sock:/var/run/docker.sock -p 7007:7007 amir20/dozzle:latest agent

See the Agent Mode documentation for more details.

Technical Details

Dozzle uses automatic API negotiation, which works with most Docker configurations. Dozzle also works with Colima and Podman.

Dozzle requires Docker Engine 19.03 or newer (API version 1.40+). Older daemons are not supported by the underlying Docker SDK.

Installation on Podman

By default, Podman doesn't have a background process, but you can enable the remote socket for Dozzle to work.

First, verify if your Podman installation has the remote socket enabled:

text
podman info

If you see output like this under the remote socket key, it's already enabled:

text
remoteSocket:
    exists: true
    path: /run/user/1000/podman/podman.sock

If it's not enabled, follow this tutorial to enable it.

Once the Podman remote socket is enabled, you can run Dozzle:

text
podman run --volume=/run/user/1000/podman/podman.sock:/var/run/docker.sock -d -p 8080:8080 docker.io/amir20/dozzle:latest

Nothing else is needed. Dozzle works out a stable host id for Podman on its own, since Podman is daemonless and has no engine identity to report.

[!NOTE]
Older versions of this page told you to create a /var/lib/docker/engine-id file to prevent host not found errors. That never worked. Podman's Docker-compatible /info endpoint does not read any file, it returns a new random UUID on every call, so Dozzle now derives the id instead. You can delete the file.

For more details, see Podman Info or the FAQ.

Security

Dozzle supports file-based authentication and forward proxy authentication with tools like Authelia. See the documentation at https://dozzle.dev/guide/authentication.

Analytics

Dozzle collects anonymous user configurations using Google Analytics. Why? Dozzle is an open source project with no funding, so there's no time for formal user studies. Analytics help prioritize features and fixes based on how people use Dozzle. This data is completely public and can be viewed live on the Data Studio dashboard.

To disable analytics, use the --no-analytics flag.

Environment Variables and Configuration

Dozzle follows the 12-factor model. Configuration can be done via CLI flags or environment variables. See the documentation at dozzle.dev/guide/supported-env-vars for more details.

Support

There are many ways to support Dozzle:

  • Use it! Write about it! Star it! If you love Dozzle, drop me a line and tell me what you love.
  • Blog about Dozzle to spread the word. If you're good at writing, send PRs to improve the documentation at dozzle.dev.
  • Sponsor my work at https://www.buymeacoffee.com/amirraminfar

License

MIT

Building

Want to contribute? Great! Dozzle has two parts: a Go backend that talks to Docker, and a Vue frontend that runs in the browser. You don't need to know both β€” pick the side that matches what you want to change. For documentation fixes, no setup is needed at all; just edit the file on GitHub.

1. Install the prerequisites

You'll need Go (1.25+), Node.js, Bun, and protoc.

On macOS, you can install everything in one go:

bash
brew install go node oven-sh/bun/bun protobuf

On Linux (Debian/Ubuntu):

bash
sudo apt install golang nodejs protobuf-compiler
curl -fsSL https://bun.com/install | bash

On Windows, we recommend using WSL2 and following the Linux instructions.

2. Clone and set up

bash
git clone https://github.com/amir20/dozzle.git
cd dozzle
bun install                 # installs frontend dependencies
go install tool             # installs Go build tools listed in go.mod (air, protoc-gen-go, etc.)
make generate               # generates TLS certificates and protobuf code (only needed once)

3. Start the dev server

bash
make dev

Open http://localhost:3100 β€” you should see the Dozzle UI connected to your local Docker. Both the frontend and backend reload automatically when you save a file.

Making your first change

Try editing assets/pages/index.vue and saving β€” the browser updates instantly. For backend changes, edit any .go file and the server will restart on its own.

Troubleshooting

  • Nothing shows up at localhost:3100 β€” make sure Docker is running and the socket is accessible at /var/run/docker.sock.
  • make generate fails β€” confirm protoc is on your PATH (protoc --version).
  • Still stuck? Open a question in GitHub Discussions β€” we're happy to help.

Adding App Icons

Dozzle shows a logo next to containers whose image it recognizes. Icons are vendored from homarr-labs/dashboard-icons into assets/icons/apps/, and matching happens in assets/utils/appIcons.ts.

The repo ships an add-app-icon skill in .claude/skills/. To add icons with an AI coding agent, paste this prompt and fill in the images:

text
Use the add-app-icon skill in .claude/skills/add-app-icon/SKILL.md to add app icons
for these container images: ,

File: docs/guide/actions.md


title: Container Actions

Container Actions

Dozzle supports container actions, which allows you to start, stop, restart, remove, and update containers from the dropdown menu on the right next to the container stats. This feature is disabled by default and can be enabled by setting the environment variable DOZZLE_ENABLE_ACTIONS to true.

The update action pulls the latest image for the container and recreates it with the same configuration β€” useful for upgrading a container in place without editing its compose file. update only has a meaningful effect when the image uses a moving tag (e.g. latest, stable); a pinned tag will simply re-pull the same image.

The old container is kept, renamed, until the new one has run for 10 seconds without restarting, and has reported healthy if its image has a healthcheck. If the new container fails to start, exits, restarts or turns unhealthy, Dozzle removes it and puts the old one back, and the update reports rolled back with the reason. The new container is labelled dev.dozzle.previous-image with the image id it replaced, and dev.dozzle.previous-ref with that image's repo@sha256:… digest (absent for images built locally). A stopped container is never updated, since it may be stopped on purpose. It still shows when a newer image is available, but Update is not offered for it, the schedule skips it, and an update asked for anyway, from Dozzle Cloud for one, is refused with "Start the container first". Start it, and it can be updated.

[!WARNING]
remove deletes the container: data in its writable layer is lost, and its anonymous volumes are left behind, detached. update recreates the container and keeps every volume, anonymous ones included, and every bind mount. Only data written to the container's writable layer is lost.

::: code-group

sh
docker run --volume=/var/run/docker.sock:/var/run/docker.sock -p 8080:8080 amir20/dozzle --enable-actions
yaml [docker-compose.yml]
services:
  dozzle:
    image: amir20/dozzle:latest
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    ports:
      - 8080:8080
    environment:
      DOZZLE_ENABLE_ACTIONS: true

:::

Update checking

Dozzle checks whether the image a container is running is still the one its registry serves. When they differ, a dot appears on the container's menu and the menu says an update is available.

The check asks the registry for the digest of the tag the container was created from, and compares it against the digest the container is actually running. It does this with a HEAD request for the image manifest, so no layers are downloaded and it does not count against Docker Hub pull rate limits. Answers are cached for six hours, and the same image is only ever looked up once no matter how many containers or hosts run it.

Because the comparison is against what the container is running, a container stays out of date until it is recreated, even if a newer image was already pulled onto the host.

Checking is separate from actions. Knowing a container is out of date is useful whether or not Dozzle is allowed to do anything about it, so the notice appears even when DOZZLE_ENABLE_ACTIONS is off. Only the Update button requires actions.

Turning it off

DOZZLE_IMAGE_CHECK_MODE controls whether Dozzle contacts registries at all.

Value Behavior
automatic Checks in the background when a container is viewed.
manual Never checks on its own. The menu offers a "Check for updates" action.
off The feature is gone. No endpoint is registered and no requests are made.

It defaults to whatever DOZZLE_RELEASE_CHECK_MODE is set to, so if you have already told Dozzle not to fetch releases automatically, it will not check images automatically either.

yaml [docker-compose.yml]
services:
  dozzle:
    image: amir20/dozzle:latest
    environment:
      DOZZLE_IMAGE_CHECK_MODE: off

To stop checking a single container, such as one deliberately pinned to a version, label it. That also keeps it off the auto-update schedule.

yaml [docker-compose.yml]
services:
  database:
    image: postgres:18-alpine
    labels:
      dev.dozzle.update: off

A notification can also be shown when an update is found. It is off by default and lives under Settings.

What cannot be checked

Some containers have nothing to compare, and Dozzle stays quiet rather than guessing:

  • Images built locally, which carry no registry digest
  • References pinned to a digest, which cannot drift
  • Private registries, since Dozzle has no credentials of its own. In Kubernetes that includes images pulled with imagePullSecrets.

Kubernetes

In Kubernetes mode the check compares the digest in the pod's status against the registry, so it works on containerd clusters such as k3s, EKS and GKE without extra permissions. The notice is informational and never comes with an Update button, because a pod's image belongs to its workload. For a moving tag like :latest with imagePullPolicy: Always, a rollout restart picks up the new image. Anything else is a change to the workload's spec. The dashboard's updates button and drawer are not shown in Kubernetes mode.

Updating Dozzle itself

The Update action on Dozzle's own container updates Dozzle in place. It pulls the new image and hands the swap to a short-lived helper container, so Dozzle goes away for a few seconds and comes back on the new version with the same configuration and volumes. It can also run on a schedule. See How self-update works for what is kept and the setups it does not cover. Running Dozzle as a Swarm service updates through the orchestrator. Dozzle agents on other hosts are ordinary containers and update like anything else.

Updating several containers at once

With actions on, the dashboard checks every container in one pass. Out-of-date containers get a small ring next to their name, and an N updates button appears above the container list. Both open the Updates drawer, which lists every container with a newer image, all selected. Untick anything you want to leave alone, then press Update.

Hosts update in parallel, and each host updates one container at a time, so no daemon has to pull a dozen images at once. The drawer shows each container moving through pulling, recreating and updated, and a failure on one container does not stop the rest. The update runs on the server, so closing the tab does not interrupt it. Opening the drawer again picks up where it is.

If Dozzle's own container is in the list, it always goes last, because updating it restarts Dozzle.

With DOZZLE_IMAGE_CHECK_MODE=manual, the button reads Check for updates until you press it. With actions off, the dashboard looks exactly as it does today, and each container's own menu still says when an update is available.

Auto-updating containers {#auto-updating-containers}

Dozzle can update containers on a schedule. Set it up under Settings β†’ Updates or in the setup wizard:

  • When: off, daily or weekly on Sunday, at a time of day. The same as DOZZLE_AUTO_UPDATE and DOZZLE_AUTO_UPDATE_TIME.
  • Which containers: Dozzle only, Labelled containers (the default) or Everything. Dozzle itself follows the schedule in all three. The same as DOZZLE_UPDATE_CONTAINERS (off, labelled or all).

One label on a container decides the rest:

dev.dozzle.update What happens
auto Updated on the schedule, unless Which containers is Dozzle only
(no label) Updated on the schedule under Everything. Otherwise checked and shown as an update, which you apply
off Never checked, never updated
yaml
services:
  app:
    image: ghcr.io/example/app:latest
    labels:
      dev.dozzle.update: auto

Older labels still work: dev.dozzle.auto-update=true reads as auto, and dev.dozzle.update-check=false as off.

At the scheduled time Dozzle checks each container on the schedule against its registry and updates only the ones with a newer image, Dozzle itself last. Every update is the safe swap described above, so a new container that fails to stay up is replaced by the old one again. Containers that are stopped or unhealthy, that Dozzle cannot check, or that were rolled back from the image on offer are skipped. Settings β†’ Updates lists the containers the next run will update.

Under Everything, a database on a floating tag like postgres:latest can move to a major version its data files cannot read. Picking Everything lists the containers that keep data in named volumes. Label those dev.dozzle.update: off to keep them out.

Which containers is saved in dozzle.yml as updateContainers, so changing it from the UI needs /data on a volume. Auto-update runs in server mode, including containers on remote agents, and needs actions on. Coming from Watchtower? See Moving from Watchtower.

Cleaning up old images {#cleaning-up-old-images}

Every update leaves the image it replaced on the host, so Dozzle removes old images after an update, like Watchtower's --cleanup. It always runs, for every update: scheduled, from a container's Update action, or from the Updates drawer. There is nothing to turn on.

Dozzle keeps the image the container ran until now, so the container can still go back to it, and removes the one before that. An update from 1.4.1 to 1.4.2 removes 1.4.0 and keeps 1.4.1, so each container keeps at most one spare image. Dozzle reads which image to remove from the old container's dev.dozzle.previous-image label, so a container's first update removes nothing.

Cleanup only runs after the update has gone through and the old container is gone. A rolled back update removes nothing. Dozzle only removes an untagged image that no container uses: an image that still has a tag, such as one you pulled or built yourself, is kept, and the removal is not forced, so Docker refuses while any other container, running or stopped, still uses it. A refusal never fails the update.

Containers on remote agents are cleaned up the same way, and so is Dozzle's own container: the self-update helper removes the image before the previous one once the new Dozzle has stayed up. Swarm services, including Dozzle running as one, are not cleaned up, since each node keeps its own images and Swarm prunes its own task history.

Rolling back {#rolling-back}

Rolling an update back is a Dozzle Cloud feature. Dozzle Cloud watches each update the schedule makes and, when the new version starts failing, offers to roll it back. Dozzle then swaps the container back to the image it ran before, the one its dev.dozzle.previous-image label names, the same way an update swaps it: the current container is kept until the previous image has stayed up, and is put back if it does not. Settings and volumes stay as they are. Nothing is pulled, so if the previous image is no longer on the host the rollback fails and the container is left alone.

The rolled back container is labelled dev.dozzle.rolled-back-from with the image it left, and the auto-update schedule leaves it alone until its tag points to a newer image. Once the rollback has stayed up, the image rolled back from is cleaned up like any old image, so it is removed only when no tag points to it any more.

Rollback works for standalone containers, including containers on remote agents. It is not available for Swarm services, Kubernetes, or Dozzle's own container.

Updates in the log view

When Dozzle updates a container, on the schedule, from the Dozzle UI or from Dozzle Cloud, the logs of the new container start with a marker that names the image it moved from and to and what started the update. A rollback and an update that was undone are marked too. With Dozzle Cloud linked, the marker also shows what Dozzle Cloud made of the update, with a link to it there. Dozzle keeps its recent updates in memory, so the marker is gone after Dozzle restarts.


File: docs/guide/agent.md


title: Agent Mode

Agent Mode

Dozzle can run in agent mode which can expose Docker hosts to other Dozzle instances. All communication is done over a secured connection using TLS. This means that you can deploy Dozzle on a remote host and connect to it from your local machine.

[!WARNING] An agent is only as private as the network it listens on
The certificate Dozzle ships with is the same in every copy of the image, so it encrypts the connection but it does not 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 does not look at DOZZLE_ENABLE_SHELL or DOZZLE_ENABLE_ACTIONS, those flags only control what the UI offers.

Keep port 7007 on a private network, and if the agent is reachable from anywhere else, generate your own certificate so agents only accept your hub.

[!NOTE] Using Docker Swarm?
If you are using Docker Swarm Mode, you don't need to use agents. Dozzle will automatically discover itself and create a cluster using swarm mode. See Swarm Mode for more information.

How to Create an Agent

To create a Dozzle agent, you need to run Dozzle with the agent subcommand. Here is an example:

::: code-group

sh
docker run -v /var/run/docker.sock:/var/run/docker.sock -p 7007:7007 amir20/dozzle:latest agent
yaml [docker-compose.yml]
services:
  dozzle-agent:
    image: amir20/dozzle:latest
    command: agent
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    ports:
      - 7007:7007

:::

[!NOTE] Docker Socket Proxy users
If you are using a remote agent you CANNOT add a socket proxy on top of the agent. Dozzle agents REPLACE using a proxy, see Remote Hosts for more info and how to use a socket proxy instead of an agent.

The agent will start and listen on port 7007. You can connect to the agent using the Dozzle UI by providing the agent's IP address and port. The agent will only show the containers that are available on the host where the agent is running.

[!TIP]
You don't need to expose port 7007 if using Docker network. The agent will be available to other containers on the same network. This is the safest way to run an agent, since nothing outside that network can reach it.

How to Connect to an Agent

To connect to an agent, you need to provide the agent's IP address and port. Here is an example:

::: code-group

sh
docker run -p 8080:8080 amir20/dozzle:latest --remote-agent agent:7007
yaml [docker-compose.yml]
services:
  dozzle:
    image: amir20/dozzle:latest
    environment:
      - DOZZLE_REMOTE_AGENT=agent:7007
    ports:
      - 8080:8080 # Dozzle UI port

:::

Note that it is not necessary to mount the local Docker socket when connecting to agents, in which case the UI will only show the containers that are available on the agents.

[!TIP]
If you want to include the host containers in the UI as well, mount the docker.sock socket as shown in the getting started example.

[!TIP]
You can connect to multiple agents by providing multiple DOZZLE_REMOTE_AGENT environment variables. For example, DOZZLE_REMOTE_AGENT=agent1:7007,agent2:7007.

[!TIP]
In server mode you can also add an agent from the UI, with Add host at the bottom of the host list, under Settings β†’ Hosts, or in the Hosts step of the setup wizard. Dozzle connects to the agent before saving it, and the host shows up without a restart. Agents added this way are saved in /data/dozzle.yml, so /data has to be on a volume. Agents set with DOZZLE_REMOTE_AGENT stay as they are and can't be removed from the UI.

Host Groups

When managing many agents across different environments, you can assign each agent to a named group. Groups appear as collapsible sections in the sidebar, and each group has a "merge all" button to view combined logs from every host in the group.

The connection string format is endpoint|name|group β€” all three parts are optional:

Format Result
agent:7007 No name override, no group
agent:7007|web-1 Name override, no group
agent:7007|web-1|Production Name override + group
agent:7007||Production Default hostname + group

::: code-group

sh
docker run -p 8080:8080 amir20/dozzle:latest \
  --remote-agent agent1:7007|web-1|Production \
  --remote-agent agent2:7007|web-2|Production \
  --remote-agent agent3:7007|dev-1|Development
yaml [docker-compose.yml]
services:
  dozzle:
    image: amir20/dozzle:latest
    environment:
      - DOZZLE_REMOTE_AGENT=agent1:7007|web-1|Production,agent2:7007|web-2|Production,agent3:7007|dev-1|Development
    ports:
      - 8080:8080

:::

The sidebar will display:

text
β–Ύ Production
    web-1
    web-2
β–Ύ Development
    dev-1
  ungrouped-host   ← agents without a group appear below

Clicking the merge icon next to a group name opens a combined log view streaming from all hosts in that group. The merged view is also available directly at /host-group/<group-name>.

Agents without a group continue to work exactly as before and appear below grouped sections.

Common Issues

Agent Not Showing Up

If you are seeing An agent with an existing ID was found. Removing the duplicate host. then you have two hosts that use the same Server ID.

Dozzle utilizes the Docker API to collect information about hosts. Each agent requires a unique host ID that remains consistent across restarts to ensure proper identification. Currently, agents identify the host using either Docker's system ID or node ID.

If you are operating in a Swarm environment, the node ID will be employed for this purpose. However, if you notice that not all hosts are visible, it may be due to the presence of duplicate hosts configured with the same host ID.

To resolve this issue, you should remove /var/lib/docker/engine-id from your system and restart. This action will help eliminate any conflicts caused by duplicate host IDs. For additional information and troubleshooting tips, please refer to the FAQ.

Advanced Options

Setting Up Healthcheck

You can set a healthcheck for the agent, similar to the healthcheck for the main Dozzle instance. When running in agent mode, healthcheck checks agent connection to Docker. If Docker is not reachable, the agent will be marked as unhealthy and will not be shown in the UI.

To set up healthcheck, use the healthcheck subcommand. Here is an example:

yml
services:
  dozzle-agent:
    image: amir20/dozzle:latest
    command: agent
    healthcheck:
      test: ["CMD", "/dozzle", "healthcheck"]
      interval: 5s
      retries: 5
      start_period: 5s
      start_interval: 5s
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    ports:
      - 7007:7007

Changing Agent's Name

Similar to Dozzle instance, you can change the agent's name by providing the DOZZLE_HOSTNAME environment variable. Here is an example:

::: code-group

sh
docker run -v /var/run/docker.sock:/var/run/docker.sock -p 7007:7007 amir20/dozzle:latest agent --hostname my-special-name
yaml [docker-compose.yml]
services:
  dozzle-agent:
    image: amir20/dozzle:latest
    command: agent
    environment:
      - DOZZLE_HOSTNAME=my-special-name
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    ports:
      - 7007:7007

:::

This will change the agent's name to my-special-name and will be reflected on the UI when connecting to the agent.

Setting Up Filters

You can set up filters for the agent to limit the containers it can access. These filters are passed directly to Docker, restricting what Dozzle can view.

yaml
services:
  dozzle-agent:
    image: amir20/dozzle:latest
    command: agent
    environment:
      - DOZZLE_FILTER=label=color
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro

This will restrict the agent to displaying only containers with the label color. Keep in mind that these filters are combined with the UI filters to narrow down the containers. To learn more about the different types of filters, read the filters documentation.

Custom Certificates

Dozzle ships with a self-signed certificate that both ends present to each other, and each end trusts that one certificate and nothing else. It is compiled into the binary, so it is identical in every Dozzle install and anyone can read it out of the public image.

That means it gives you encryption but no authentication. An agent cannot tell your hub apart from someone else's, so the only thing keeping strangers out of an agent started with the defaults is that they cannot reach port 7007.

[!WARNING] When you need your own certificate
If port 7007 is reachable by anything you do not control, which includes publishing it on a host with a public IP, generate your own pair. Every deployment that does gets a credential nobody else holds.

Run generate-certs to write a unique pair:

sh
docker run --rm -v "$PWD":/out amir20/dozzle:latest generate-certs --cert-out /out/dozzle_cert.pem --key-out /out/dozzle_key.pem

Copy both files to the hub and to every agent. Dozzle looks for them at /dozzle_cert.pem and /dozzle_key.pem, so mounting them there is enough. You can put them elsewhere with the --cert and --key flags or the DOZZLE_CERT and DOZZLE_KEY environment variables.

Treat the key like a password. Anyone holding it can connect to your agents, and an agent will reject a hub presenting anything else, so the hub and its agents have to be given the same pair and restarted together.

Here is an example using the default paths:

yml
services:
  agent:
    image: amir20/dozzle:latest
    command: agent
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    secrets:
      - source: cert
        target: /dozzle_cert.pem
      - source: key
        target: /dozzle_key.pem
    ports:
      - 7007:7007
secrets:
  cert:
    file: ./cert.pem
  key:
    file: ./key.pem

Or using custom paths with environment variables:

yml
services:
  agent:
    image: amir20/dozzle:latest
    command: agent
    environment:
      - DOZZLE_CERT=/certs/my-cert.pem
      - DOZZLE_KEY=/certs/my-key.pem
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./certs:/certs
    ports:
      - 7007:7007

Or using command-line flags:

::: code-group

sh
docker run -v /var/run/docker.sock:/var/run/docker.sock -v ./certs:/certs -p 7007:7007 amir20/dozzle:latest agent --cert /certs/my-cert.pem --key /certs/my-key.pem
yaml [docker-compose.yml]
services:
  agent:
    image: amir20/dozzle:latest
    command: agent --cert /certs/my-cert.pem --key /certs/my-key.pem
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./certs:/certs
    ports:
      - 7007:7007

:::

[!TIP]
Docker secrets are preferred for providing certificates. They can be created using docker secret create command or as the example above using docker-compose.yml. The same certificates should be provided to the Dozzle instance connecting to the agent.

This will mount the certificate and key files to the agent. The agent will use these certificates for communication. The same certificates should be provided to the Dozzle instance connecting to the agent.

If you would rather generate them with openssl than with generate-certs:

sh
$ openssl genpkey -algorithm Ed25519 -out key.pem
$ openssl req -new -key key.pem -out request.csr -subj "/C=US/ST=California/L=San Francisco/O=My Company"
$ openssl x509 -req -in request.csr -signkey key.pem -out cert.pem -days 365

Private certificate for agents added from the UI {#private-certificate}

When you add an agent with Add host, the dialog has a Private certificate toggle. It is on by default for new hosts. With it on, only this Dozzle can connect to the agent, even if port 7007 is reachable by others.

The first time it is used, Dozzle creates its own pair in /data/agent_cert.pem and /data/agent_key.pem. The pair is never replaced, so every private agent keeps working across restarts and updates. The compose snippet in the dialog passes the pair to the agent through DOZZLE_CERT_PEM and DOZZLE_KEY_PEM:

yaml [docker-compose.yml]
services:
  dozzle-agent:
    image: amir20/dozzle:vX.Y.Z # same tag as your Dozzle
    command: agent
    environment:
      DOZZLE_CERT_PEM: |
        -----BEGIN CERTIFICATE-----
        ...
        -----END CERTIFICATE-----
      DOZZLE_KEY_PEM: |
        -----BEGIN PRIVATE KEY-----
        ...
        -----END PRIVATE KEY-----
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    ports:
      - 7007:7007

The snippet contains the private key, so treat it like a password. The toggle is per host: agents that already exist, and any agent added with the toggle off, keep using the certificate they have. Dozzle records which agents are private under privateAgents in /data/dozzle.yml. The toggle is not offered when Dozzle already runs a custom certificate, because every agent needs that pair anyway. The snippet uses the same image as your Dozzle. An older agent ignores DOZZLE_CERT_PEM and DOZZLE_KEY_PEM, presents the built-in certificate, and is refused.

Comparing Agents with Remote Connection

Agents are similar to remote connections, but they have some advantages. Generally, agents are preferred over remote connections due to performance and security reasons. Here is a comparison:

Feature Agent Remote Connection
Performance Better with distributed load Worse on the UI
Security Encrypted, bring your own cert Insecure or Docker TLS
Ease of use Easy out of the box Requires exposing Docker socket
Permissions Full access to Docker Can be controlled with a proxy
Reconnect Automatically reconnects Requires UI restart
Healthcheck Built-in healthcheck No healthcheck
Filters Supports filters No support for filters

If you do plan to use remote connections, make sure to secure the connection using Docker TLS or a reverse proxy.


File: docs/guide/analytics.md


title: Anonymous Analytics

Data Collection of Analytics

Dozzle collects anonymous usage data via a lightweight beacon to help prioritize features and fixes. It is an open-source project with no funding, so this data is the primary signal for where to invest effort.

What is Collected

At a high level, the beacon includes things like the Dozzle version, deployment mode (server, swarm, k8s, agent), which auth provider is enabled, a few feature flags, the Docker Engine version, and small counts (number of hosts, containers, filters). A random per-install ID is included for deduplication.

No log contents, container names, image names, IP addresses, or user identifiers are ever transmitted. The exact set of fields evolves over time β€” the authoritative source is types/beacon.go, and the sender is internal/analytics/http_beacon.go.

Where is Data Stored

Events are posted to https://b.dozzle.dev/event, a small Go service that writes events to a flat file on DigitalOcean for later processing.

Opting Out

Pass --no-analytics or set DOZZLE_NO_ANALYTICS=true. No beacon requests will be made.

yaml
services:
  dozzle:
    image: amir20/dozzle:latest
    environment:
      DOZZLE_NO_ANALYTICS: "true"

File: docs/guide/authentication.md


title: Authentication

Authentication

Dozzle supports two configurations for authentication. In the first configuration, you bring your own authentication method by protecting Dozzle through a proxy. Dozzle can read appropriate headers out of the box.

If you do not have an authentication solution, then Dozzle has a simple file-based user management solution. Authentication providers are set up using the --auth-provider flag. In both configurations, Dozzle will try to save user settings to disk. This data is written to /data.

Security Considerations

Dozzle has access to docker.sock, which β€” unless restricted β€” is equivalent to root on the host. Before exposing Dozzle beyond your private network, review the following:

  • Always put Dozzle behind authentication if it is reachable from the public internet. Use --auth-provider=simple or a forward-proxy like Authelia / Authentik / Cloudflare Access.
  • Keep actions and shell access disabled unless you need them. They allow starting, stopping, recreating, and executing arbitrary commands inside containers.
  • Restrict users with roles and filters in multi-user mode. Without explicit roles, a user can see every container the Dozzle instance can.
  • Never expose Dozzle's port directly in forward-proxy mode. Dozzle trusts Remote-User on every request, and when no roles header is present the user is granted all roles. Anyone who can reach the container without passing through the proxy authenticates as whoever they like by setting one header. Publish only the proxy, and keep Dozzle on an internal network with expose rather than ports.
  • Run TLS at the reverse proxy. See Reverse Proxy & Base Path for Nginx / Traefik / Caddy examples.
  • Restrict docker.sock access with a proxy if you don't need actions. Note that a read-only mount (/var/run/docker.sock:/var/run/docker.sock:ro) does not limit the API: the :ro flag only marks the socket file read-only on disk, while API calls still pass through the socket normally, so create/delete/update remain possible. To actually restrict operations, put a socket proxy like tecnativa/docker-socket-proxy in front of the daemon.

Choosing a method

Method Who owns the users Use it when
Simple Dozzle, in users.yml You have no authentication solution and want Dozzle to handle logins.
GitHub & OIDC Dozzle, in users.yml You want the same users.yml users to sign in with GitHub, Google, Keycloak, Pocket ID, Zitadel or Authentik instead of a password.
OpenID Connect Your identity provider You want Keycloak, Authentik, Zitadel or Pocket ID to own the user list, with roles and filters read from the token and no users.yml.
Forward Proxy Your proxy You already run Authelia, Authentik, Cloudflare Access or similar, and want it to own authentication entirely.

Simple and OAuth are the same provider: users.yml is the user list either way, and OAuth only adds a second way to prove you are one of the users in it. OpenID Connect and forward proxy are the separate ones, where something outside Dozzle owns the users. Pick oidc when your identity provider can put roles in the token, and forward proxy when you need org-wide or domain-wide access rules enforced in front of Dozzle, which users.yml deliberately does not do.

Generating users.yml

Dozzle has a built-in generate command to generate users.yml. Here is an example:

sh
docker run -it --rm amir20/dozzle generate admin --password password --email [email protected] --name "John Doe" --user-filter name=foo --user-roles shell > users.yml

In this example, admin is the username. Email and name are optional but recommended to display accurate avatars. docker run -it --rm amir20/dozzle generate --help displays all options. The --user-filter flag is a comma-separated list of filters. The --user-roles flag is a comma-separated list of roles.

If you omit --password, Dozzle prompts for it on stdin so the password never lands in your shell history. This requires an interactive terminal, so keep the -it flags:

sh
docker run -it --rm amir20/dozzle generate admin --email [email protected] --name "John Doe" > users.yml

The prompt is written to stderr, so redirecting stdout to users.yml still works. You can also pipe the password in, for example echo "$PASSWORD" | docker run -i --rm amir20/dozzle generate admin > users.yml.


File: docs/guide/changing-base.md


title: Reverse Proxy & Base Path

Reverse Proxy & Base Path

Dozzle is commonly placed behind a reverse proxy for TLS termination, authentication, or to share a hostname with other services. This page covers both mounting Dozzle at a sub-path and the proxy settings needed to make streaming work correctly.

Changing the Base Path

Dozzle by default mounts to /. This can be changed with the --base flag or the DOZZLE_BASE environment variable. For example, to mount at /foobar:

::: code-group

sh
docker run --volume=/var/run/docker.sock:/var/run/docker.sock -p 8080:8080 amir20/dozzle --base /foobar
yaml [docker-compose.yml]
services:
  dozzle:
    image: amir20/dozzle:latest
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    ports:
      - 8080:8080
    environment:
      DOZZLE_BASE: /foobar

:::

Dozzle will be available at http://localhost:8080/foobar/. This option rewrites all assets to /foobar/{file.path} and automatically redirects /foobar to /foobar/.

Proxy Requirements

Dozzle streams logs over Server-Sent Events (SSE) and uses WebSocket for container shells. Reverse proxies must:

  1. Disable response buffering β€” SSE delivers events as they happen. Any buffering causes logs to arrive in bursts or never arrive at all. Dozzle sends X-Accel-Buffering: no, but some proxies ignore it.
  2. Forward WebSocket upgrade headers β€” required for the shell and attach features.
  3. Avoid compressing text/event-stream β€” compression middleware often breaks SSE.

Nginx

nginx
location ^~ /foobar/ {
    proxy_pass http://dozzle: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";
}

Drop the ^~ /foobar/ prefix if Dozzle is mounted at the root. See also the FAQ entry on disabling buffering.

Traefik

Traefik handles WebSocket upgrades automatically, but the default compress middleware will break SSE. Exclude text/event-stream:

yaml
http:
  middlewares:
    middlewares-compress:
      compress:
        excludedContentTypes:
          - text/event-stream

Then a typical labels block on the Dozzle service:

yaml
services:
  dozzle:
    image: amir20/dozzle:latest
    labels:
      - traefik.enable=true
      - traefik.http.routers.dozzle.rule=Host(`dozzle.example.com`)
      - traefik.http.routers.dozzle.entrypoints=websecure
      - traefik.http.routers.dozzle.tls.certresolver=letsencrypt
      - traefik.http.services.dozzle.loadbalancer.server.port=8080

Caddy

caddyfile
dozzle.example.com {
    reverse_proxy dozzle:8080 {
        flush_interval -1
    }
}

flush_interval -1 disables response buffering for streaming endpoints.

Common Pitfalls

  • Blank page or assets 404 when using --base β€” the proxy is stripping the path prefix before forwarding. Configure it to pass the full path through to Dozzle.
  • Logs stop after a few seconds β€” connection timeouts on the proxy are too short. Increase read/send timeouts to at least a few minutes (e.g. Nginx proxy_read_timeout 3600s).
  • Shell disconnects immediately β€” WebSocket upgrade headers are not being forwarded. Verify Upgrade and Connection headers.

File: docs/guide/container-groups.md


title: Container Groups

Container Groups

Dozzle performs automatic grouping of containers based on their stack name or service name. You can also create custom groups using labels.

Default Groups

By default, containers are grouped by their stack name in host mode. If com.docker.swarm.service.name label is present, Dozzle will automatically enable a "swarm mode" where all containers with the same service name will be joined together.

Custom Groups

Additionally, you can create custom groups by adding a label to your container. The label is dev.dozzle.group and the value is the name of the group. All containers with the same group name will be joined together in the UI. For example, if you have a group named myapp, all containers with the label dev.dozzle.group=myapp will be joined together.

Here is an example using Docker Compose or Docker CLI:

::: code-group

sh
docker run --label dev.dozzle.group=myapp hello-world
yaml [docker-compose.yml]
services:
  dozzle:
    image: hello-world
    labels:
      - dev.dozzle.group=myapp

:::

Kubernetes

In Kubernetes mode, the sidebar groups pods by namespace and owner. A dev.dozzle.group label or annotation on the pod template adds every container in the pod to a custom group as well. A label and an annotation behave the same, so use an annotation if the group name has spaces. When both are set, the annotation wins.

yaml [deployment.yaml]
spec:
  template:
    metadata:
      labels:
        dev.dozzle.group: myapp

File: docs/guide/container-names.md


title: Container Names

Container Names

By default, Dozzle retrieves container names directly from Docker. This is usually sufficient, as these names can be customized using the --name flag in docker run commands or through the container_name field in Docker Compose services.

Custom Names

In cases where modifying the container name itself isn't possible, you can override it by adding a dev.dozzle.name label to your container.

Here is an example using Docker Compose or Docker CLI:

::: code-group

sh
docker run --label dev.dozzle.name=hello hello-world
yaml [docker-compose.yml]
services:
  dozzle:
    image: hello-world
    labels:
      - dev.dozzle.name=hello

:::

Kubernetes

In Kubernetes mode, containers are named /<container> by default. Set dev.dozzle.name on the pod template to override it. An annotation is the better place for it, because label values cannot contain spaces and are limited to 63 characters. If both are set, the annotation wins. The same goes for every other dev.dozzle.* setting, such as dev.dozzle.url and dev.dozzle.icon.

yaml [deployment.yaml]
spec:
  template:
    metadata:
      annotations:
        dev.dozzle.name: Public API

The name applies to the whole pod. Init containers always keep their own name as a suffix, for example Public API/migrate, and so does every container in a pod with more than one app container, for example Public API/proxy.

Replicas of the same deployment all get the same name, and Dozzle treats containers with the same name as one. Pinning one replica in the sidebar pins all of them, and the container title menu lists the others as earlier runs of the same container.

Coolify Integration

If you're using Coolify, Dozzle automatically recognizes Coolify's labels as fallbacks:

  • coolify.serviceName β†’ Used as container name if dev.dozzle.name is not set
  • coolify.projectName β†’ Used for grouping if dev.dozzle.group is not set

No additional configuration is needed for Coolify deployments.


File: docs/guide/debugging.md


title: Debugging

Debugging with Logs

By default Dozzle logs at info level, which is intentionally quiet. When something isn't working, turn up the verbosity using the --level flag or the DOZZLE_LEVEL environment variable.

Level When to use
info Default. Startup details, errors, and warnings.
debug Request-level diagnostics, auth decisions, agent connections, config dump.
trace Everything. Individual log events, beacon payloads, gRPC frames. Very noisy.

Dozzle writes all logs to stdout, so docker logs dozzle is the right place to read them.

yaml
services:
  dozzle:
    image: amir20/dozzle:latest
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    ports:
      - 8080:8080
    environment:
      DOZZLE_LEVEL: debug

Reporting a Bug

If you think you've hit a bug, please open an issue at github.com/amir20/dozzle/issues. Include:

  • Dozzle version (visible in the UI footer or dozzle --version)
  • Deployment mode: server, swarm, k8s, or agent
  • Docker or Kubernetes version
  • Relevant debug- or trace-level log output
  • Steps to reproduce, ideally with a minimal docker-compose.yml

The more context in the initial report, the faster it can be triaged.


File: docs/guide/faq.md


title: FAQ

Frequently Asked Questions

Dozzle fails to start with `client version 1.x is too new`. What does this mean?

Dozzle requires Docker Engine 19.03 or newer (API version 1.40+). Older daemons β€” for example Docker 18.06 (API 1.38) β€” are not supported by the underlying Docker SDK and will fail at startup with an error like failed to create docker client: ... client version 1.54 is too new. Maximum supported API version is 1.38.

Upgrade Docker Engine to a supported version. As a temporary workaround, pin Dozzle to v10.5.2 or earlier, which used a Docker SDK that still negotiated down to older API versions.

How do I upgrade Dozzle?

Dozzle follows standard Docker image practices. To upgrade, pull the new image and recreate the container:

sh
docker pull amir20/dozzle:latest
docker compose up -d dozzle

User settings, notification rules, and other state are stored in /data (see below), so keep that volume mounted across upgrades. For production use, pin a specific tag (e.g. amir20/dozzle:v10.9.2) rather than latest so upgrades are deliberate. Release notes are published on the GitHub releases page. Rolling back is as simple as redeploying an older tag.

My platform wraps the container entrypoint and Dozzle fails with `no such file or directory`

The default image is built FROM scratch, so it contains the Dozzle binary and nothing else. No shell, no interpreter.

Some platforms add optional features by bind-mounting a #!/bin/sh wrapper over the container entrypoint and re-exec'ing the original one. Unraid's per-container Tailscale toggle works this way, as do some sidecar and init injectors. Without /bin/sh in the image the wrapper cannot be executed and the container exits with an error naming the wrapper rather than the missing shell:

text
exec /opt/unraid/tailscale: no such file or directory

For these cases use the alpine variant, which is the same binary on an Alpine base:

sh
docker run \
  --volume=/var/run/docker.sock:/var/run/docker.sock \
  -p 8080:8080 \
  amir20/dozzle:alpine

Versioned tags follow the same pattern (amir20/dozzle:v10.9.2-alpine). The scratch-based latest stays the recommended image for everything else, since it has a much smaller footprint and no distro packages to patch.

What is stored in `/data` and how do I back it up?

The /data directory is where Dozzle persists anything that needs to survive a container restart:

  • users.yml / users.yaml β€” simple-auth user file (if you created one)
  • Notification rules, destinations, and delivery state
  • Per-user UI settings (only in multi-user mode; single-user settings live in browser localStorage)
  • A small set of internal files like dismissed-announcement state

The directory is small (typically well under 10 MB) and can be backed up with a simple tar or rsync of the mounted volume. When upgrading or migrating to a new host, moving the /data volume carries all settings with it.

I installed Dozzle, but logs are slow or they never load. What do I do?

Dozzle uses Server Sent Events (SSE) which connects to a server using a HTTP stream without closing the connection. If any proxy tries to buffer this connection, then Dozzle never receives the data and hangs forever waiting for the reverse proxy to flush the buffer. Since version 1.23.0, Dozzle sends the X-Accel-Buffering: no header which should stop reverse proxies buffering. However, some proxies may ignore this header. In those cases, you need to explicitly disable any buffering.

Disabling buffering in nginx

Below is an example with nginx and using proxy_pass to disable buffering:

text
server {
    ...

    location / {
        proxy_pass                  http://<dozzle.container.ip.address>:8080;
    }

    location /api {
        proxy_pass                  http://<dozzle.container.ip.address>:8080;

        proxy_buffering             off;
        proxy_cache                 off;
    }
}

Disabling compression in traefik

Traefik reverse proxy can be configured via middlewares to support compression. If implemented, the usual configuration looks like this:

text
http:
  middlewares:
    middlewares-compress:
      compress: {}

With this setup, you may find that certain containers do not show logs in dozzle anymore if you open dozzle via traefik (e.g., dozzle.mydomain.com).
You will also note that the same dozzle instance does show the logs when accessed directly (e.g., localhost:8080).

Containers where this has been observed (non-exhaustive list) are: dozzle, homepage, glances, filebrowser.

To re-enable the logs to flow, exclude text/event-stream from the compression middleware:

text
http:
  middlewares:
    middlewares-compress:
      compress:
        excludedContentTypes:
          - text/event-stream

Dozzle has a special route that can be used to search containers by name and then forward to that container. For example, if you have a container with name "foo.bar" and id abc123, you can send your users to /show?name=foo.bar which will be forwarded to /container/abc123.

To open several containers in one merged view, separate the names with commas: /show?name=foo,bar forwards to /merged/. When more than one container has the same name, the most recently started one wins. Add host=<host id> to only match containers on that host, e.g. /show?name=foo,bar&host=abc. Names that match nothing are skipped.

I installed Dozzle but memory consumption doesn't show up!

This is an issue specific to ARM devices.

Dozzle uses the Docker API to gather information about the containers' memory usage. If the memory usage is not showing up, then it is likely that the Docker API is not returning the memory usage.

You can verify this by running docker info, and you should see the following:

text
WARNING: No memory limit support
WARNING: No swap limit support

In this case, you'll need to add the following line to your /boot/cmdline.txt file and reboot your device:

text
cgroup_enable=cpuset cgroup_enable=memory cgroup_memory=1

I am seeing duplicate hosts error in the logs. How do I fix it?

If you are seeing the following error in the logs, then you may have duplicate hosts configured with the same host ID:

text
time="2024-07-10T13:35:53Z" level=warning msg="duplicate host ID: *********, Endpoint: 1.1.1.1:7007 found, skipping"

Dozzle uses the Docker API to gather information about the hosts. Each host must have a unique ID. This ID is used to identify the host in the UI. In swarm mode, Dozzle uses the node ID from docker system info to identify the host. If you are not using swarm mode, then Dozzle will use the system ID from docker system info as the host ID.

Sometimes, VMs may be restored from backups with the same host ID. This can cause Dozzle to think that the host is already present and skip adding it to the list of hosts. To fix this, you need to remove /var/lib/docker/engine-id file. This file contains the host ID and is created when the Docker daemon starts.

I am seeing host not found error in the logs. How do I fix it?

This is mainly a Podman error. Podman is daemonless and tracks no engine identity, so its Docker-compatible /info endpoint returns a brand new random UUID on every call. Dozzle reads that ID once when it connects and uses it to identify the host, so every restart used to produce a different host, and the main server kept routing to an ID that no longer answered.

Dozzle now derives a stable ID on Podman from the hostname and the container storage path, so this should fix itself once both the server and the agents are updated. If you are still seeing it, restart the main Dozzle server so it picks up the new IDs.

[!WARNING]
Earlier versions of this page told you to create /var/lib/docker/engine-id. That never had any effect on Podman, which reads no file for this. You can safely delete it.

If you are using Docker rather than Podman, check that engine-id exists in /var/lib/docker with a UUID inside and is readable by the Docker daemon.

Two Podman hosts that share both a hostname and a storage path will still collide, since that is all Podman gives Dozzle to work with. Set DOZZLE_HOST_ID on one of them to break the tie:

sh
podman run -e DOZZLE_HOST_ID=web-01 ...

See the Podman guide for the full setup.

Why am I only seeing running containers? How do I see stopped containers?

By default, Dozzle only shows running containers. To see stopped containers, you need to enable the Show Stopped Containers option in the settings. This option is disabled by default to reduce the number of containers shown in the UI.

Is there a way to sync my settings across multiple instances of Dozzle?

In single-user mode, Dozzle stores the settings in the browser's local storage. This means that the settings are only available on the browser where they were set. For Dozzle to enable syncing settings across multiple instances, it needs to know who the user is. In multi-user mode, Dozzle uses the user's username to store the settings on disk and sync them across multiple instances. This information is stored in /data directory. If you want to sync settings across multiple instances, you need to enable multi-user mode and provide a username.

Why doesn't Dozzle support notifications for Slack, Discord, Telegram, email, etc. directly?

By design, Dozzle is unopinionated about where your alerts go. Instead of bundling integrations for specific notification platforms, Dozzle provides webhooks with customizable payload templates. This means you can send alerts to any service that accepts HTTP requests β€” Slack, Discord, Telegram, ntfy, PagerDuty, Opsgenie, or your own internal tools β€” without waiting for Dozzle to add explicit support.

There are a few reasons for this approach:

  • Universality. Webhooks work with virtually every notification platform. Adding provider-specific integrations would cover only a fraction of what users need, while webhooks cover all of them.
  • Maintenance. Each provider integration brings its own API quirks, authentication flows, rate limits, and breaking changes. Supporting them means Dozzle maintainers become responsible for debugging issues with third-party services β€” which is outside the scope of a log viewer.
  • Simplicity. Dozzle is a lightweight, focused tool for viewing Docker logs. Keeping the notification layer generic keeps the codebase small and the project sustainable.

If you need a more opinionated experience with richer provider integrations (e.g., web push notifications, ntfy action buttons), Dozzle Cloud is designed for that.

For setting up webhooks with your preferred service, see the Alerts & Webhooks guide β€” it includes built-in payload templates for Slack, Discord, and ntfy that you can use as-is or customize.

Why does Dozzle keep dockerd and containerd at slightly elevated CPU when no browser is connected?

Dozzle keeps container stats streaming for up to 6 hours (2 hours on Kubernetes) after the last browser disconnects, then shuts the stats collector down on its own. This is intentional. Stats are streamed continuously so that when you reopen the UI you see historical CPU and memory history instead of a blank chart. If streaming stopped the moment you closed the tab, there would be no history to show.

The cost is a small, steady amount of CPU in dockerd and containerd, since Docker's stats API is polling based. Restarting the Dozzle container resets the timer immediately, which is why a restart drops the host back to idle. This is not configurable by design. A small timeout would break other functionality that assumes stats are still streaming, so lowering it defeats the purpose of the stats history.

My Dozzle instances are timing out in Swarm Mode or I'm not seeing all my Swarm nodes when behind a load balancer. How do I fix it?

In Swarm Mode, Dozzle instances may require their own overlay network. If you see inconsistent behavior when connecting to different Dozzle nodes, consider adding a separate overlay network which only contains the Dozzle instances, as shown below:

text
services:
  logs:
    ...
    networks: [ traefik, dozzle ]
    ...

networks:
  dozzle:
    driver: overlay
  traefik:
    external: true

The external network traefik is the overlay network which is used for the load balancer service discovery, and we've created a new dozzle overlay network for the Dozzle nodes to talk to one another.