---
url: https://dozzle.dev/guide/agent.md
description: >-
  Run Dozzle in agent mode to view logs from remote Docker hosts over TLS,
  organize hosts into groups, and lock agents down with your own certificate.
---

# 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](#custom-certificates) 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](/guide/swarm-mode) for more information.

## &#x20;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](/guide/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.

## &#x20;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](/guide/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](/guide/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.

## &#x20;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:

```
▾ 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.

## &#x20;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](/guide/faq#i-am-seeing-duplicate-hosts-error-in-the-logs-how-do-i-fix-it).

## &#x20;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](/guide/filters#ui-agents-and-user-filters).

### 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](#custom-certificates), 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.

## &#x20;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.
