If you run a Linux server with Docker, Wazuh can monitor both the host and every container running on it. In this guide you'll see exactly what to configure, which alerts get generated, and how to detect a suspicious interactive access to a container.
What can Wazuh monitor on a Docker server?
When you install the Wazuh agent on the Linux host running Docker, you get four layers of visibility:
- Docker API events (start, stop, exec, image pulls…) through the
docker-listenermodule - Container logs collected from the host filesystem
- File integrity (FIM) over the Docker configuration:
/etc/docker, socket, binaries - Image vulnerabilities through integration with Trivy or Grype
All of this without installing any agent inside the containers.
Prerequisites
- Wazuh Manager 4.7 or later
- Wazuh agent installed on the Linux host with Docker
- Docker Engine installed
- Python 3.8–3.12 on the host
Step 1: How do you install the Python dependencies for the wodle?
The docker-listener module is a Python script that uses the docker library to connect to the daemon API. Without this step the wodle won't start. The exact versions matter:
On Python 3.8–3.10:
pip3 install docker==7.1.0 urllib3==1.26.20 requests==2.32.2 On Python 3.11–3.12 (systems with a managed environment, like Ubuntu 23+):
pip3 install docker==7.1.0 urllib3==1.26.20 requests==2.32.2 --break-system-packages Verify the installation works:
python3 -c "import docker; c = docker.from_env(); print(c.ping())"
# Should return: True Step 2: How do you grant the wazuh user access to the Docker socket?
The wodle runs as the wazuh user. So it can access the Docker socket:
sudo usermod -aG docker wazuh Apply the group change without rebooting:
sudo systemctl restart wazuh-agent Step 3: How do you enable the Docker Listener (wodle)?
The docker-listener module connects to the Docker API and captures, in real time, every daemon event: container creation, starts, stops, command execution, image pulls, network connections, and more.
Add this to the agent's /var/ossec/etc/ossec.conf:
<wodle name="docker-listener">
<interval>10m</interval>
<attempts>5</attempts>
<run_on_start>yes</run_on_start>
<disabled>no</disabled>
</wodle> Once active, this module captures events such as:
create,start,stop,die,destroy— container lifecycleexec_create,exec_start— commands run inside a containerpull,push,tag,delete— image operationsconnect,disconnect— network changescommit,export— operations that generate new images
The Wazuh rules for these events are in /var/ossec/ruleset/rules/0560-docker_integration_rules.xml (IDs 87901–87920).
Step 4: How do you collect the container logs?
Docker, with the default driver (json-file), stores each container's logs in:
/var/lib/docker/containers/<ID_CONTENEDOR>/<ID_CONTENEDOR>-json.log Add this block to ossec.conf so Wazuh reads them:
<localfile>
<log_format>syslog</log_format>
<location>/var/lib/docker/containers/*/*-json.log</location>
</localfile> Use log_format syslog even though the files are JSON. Wazuh will read each full line and the decoders will extract the fields. If you use log_format json, the parser will try to interpret Docker's wrapper (which has log, stream, and time fields) and may break on the actual log content.
If in your environment the containers use the journald driver, the configuration changes:
<localfile>
<log_format>journald</log_format>
<location>journald</location>
</localfile> Step 5: How should FIM watch the Docker configuration?
The file integrity module (syscheck) should monitor Docker's configuration files, but not the entire /var/lib/docker directory: it can grow to hundreds of GB and changes constantly with the overlay2 layers, generating thousands of false alerts.
Recommended configuration on the agent:
<syscheck>
<!-- Docker daemon configuration -->
<directories realtime="yes" check_all="yes" report_changes="yes">/etc/docker</directories>
<!-- Docker socket: detect permission changes -->
<directories realtime="yes" check_all="yes">/run/docker.sock</directories>
<!-- Docker binaries -->
<directories check_all="yes">/usr/bin/docker</directories>
<directories check_all="yes">/lib/systemd/system/docker.service</directories>
<!-- Exclude data directories (highly volatile) -->
<ignore>/var/lib/docker/overlay2</ignore>
<ignore>/var/lib/docker/containers</ignore>
<ignore>/var/lib/docker/tmp</ignore>
</syscheck> Any change to /etc/docker/daemon.json or to the socket permissions will generate a FIM alert in Wazuh.
Step 6: Restart the agent and verify
# Restart the agent
sudo systemctl restart wazuh-agent
# Check that the wodle starts without errors
sudo tail -f /var/ossec/logs/ossec.log | grep docker You should see something like:
INFO: wazuh-modulesd:docker-listener: Module Docker Listener started.
INFO: wazuh-modulesd:docker-listener: Listening to Docker events. Which key alerts will you see in the dashboard?
With this configuration, the Wazuh dashboard will show alerts like these:
| Rule ID | Level | Event |
|---|---|---|
| 87901 | 3 | Container created |
| 87903 | 3 | Container started |
| 87904 | 3 | Container stopped |
| 87907 | 5 | Command run inside a container |
| 87911 | 5 | Container removed with kill |
| 87912 | 3 | Docker image pulled |
| 87918 | 5 | Container turned into an image (commit) |
Hands-on case: detecting a suspicious docker exec
The most security-relevant event is when someone runs an interactive shell inside a container. Simulate it like this:
# Spin up a test container
docker run -d --name test_nginx nginx:latest
# Run an interactive shell (this is what we want to detect)
docker exec -it test_nginx /bin/sh Wazuh captures two events: exec_create and exec_start. The second triggers rule 87907 at level 5. To raise it to level 12 and map it to MITRE ATT&CK T1609, add this custom rule to the manager's /var/ossec/etc/rules/local_rules.xml:
<group name="docker,shell_access">
<rule id="100110" level="12">
<if_sid>87907</if_sid>
<field name="docker.Action">exec_start: (/bin/sh|/bin/bash|/bin/zsh|sh|bash)</field>
<description>Docker: Interactive shell launched in container $(docker.Actor.Attributes.name)</description>
<options>no_full_log</options>
<group>docker_shell,attack,T1609</group>
</rule>
<!-- Privileged container: risk of escaping to the host -->
<rule id="100111" level="14">
<if_sid>87901</if_sid>
<field name="docker.Actor.Attributes.privileged">true</field>
<description>Docker: Privileged container created - $(docker.Actor.Attributes.name)</description>
<group>docker_privileged,pci_dss_10.6.1</group>
</rule>
</group> Apply the changes on the manager:
sudo /var/ossec/bin/wazuh-logtest # verify the rule has no errors
sudo systemctl restart wazuh-manager Extra: SCA Docker CIS Benchmark
Wazuh doesn't include the Docker CIS policy by default in its standard installation (unlike the policies for Ubuntu or RHEL), but you can add it manually. The official Wazuh blog publishes a YAML file with the 98 controls of the CIS Docker Benchmark v1.7.0.
Once you have the cis_docker_bench.yml file, enable it in the agent's ossec.conf:
<sca>
<enabled>yes</enabled>
<scan_on_start>yes</scan_on_start>
<interval>12h</interval>
<skip_nfs>yes</skip_nfs>
<policies>
<policy>/var/ossec/etc/sca/cis_docker_bench.yml</policy>
</policies>
</sca> This will check things like: whether containers run as root, whether the rootfs is read-only, whether sensitive host volumes are mounted, or whether the Docker socket is exposed inside any container.
Extra: Scanning image vulnerabilities with Trivy
Wazuh's vulnerability-detection module analyzes the agent's OS packages, but not the internal layers of the Docker images. For that it integrates with Trivy:
# Install Trivy on the host
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | \
gpg --dearmor | sudo tee /usr/share/keyrings/trivy.gpg > /dev/null
echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] \
https://aquasecurity.github.io/trivy-repo/deb generic main" | \
sudo tee /etc/apt/sources.list.d/trivy.list
sudo apt-get update && sudo apt-get install trivy
# Scan an image manually
trivy image nginx:latest You can then use the command wodle to run Trivy periodically and send the results to Wazuh as events, where custom rules will turn them into level 7 (HIGH) or 12 (CRITICAL) alerts.
Full agent configuration
A summary of all the blocks you add to the Docker host's /var/ossec/etc/ossec.conf:
<!-- 1. Docker Listener -->
<wodle name="docker-listener">
<interval>10m</interval>
<attempts>5</attempts>
<run_on_start>yes</run_on_start>
<disabled>no</disabled>
</wodle>
<!-- 2. Container logs -->
<localfile>
<log_format>syslog</log_format>
<location>/var/lib/docker/containers/*/*-json.log</location>
</localfile>
<!-- 3. FIM -->
<syscheck>
<directories realtime="yes" check_all="yes" report_changes="yes">/etc/docker</directories>
<directories realtime="yes" check_all="yes">/run/docker.sock</directories>
<directories check_all="yes">/usr/bin/docker</directories>
<ignore>/var/lib/docker/overlay2</ignore>
<ignore>/var/lib/docker/containers</ignore>
<ignore>/var/lib/docker/tmp</ignore>
</syscheck> Frequently Asked Questions
What can Wazuh monitor on a Docker server?
Installing the Wazuh agent on the Linux host gives you four layers of visibility without any agent inside the containers: Docker API events (start, stop, exec, image pulls) through the docker-listener module, container logs collected from the host filesystem, file integrity (FIM) over the Docker configuration, and image vulnerabilities through integration with Trivy or Grype.
Do you need to install a Wazuh agent inside each container?
No. A single Wazuh agent on the Docker host monitors the daemon and every container running on it. The docker-listener module talks to the Docker API, container logs are read from /var/lib/docker/containers on the host, and FIM watches the Docker configuration. You never install anything inside the containers themselves.
Why use log_format syslog instead of json for Docker container logs?
Even though Docker's json-file driver writes JSON, you should use log_format syslog so Wazuh reads each full line and lets its decoders extract the fields. If you set log_format json, the parser tries to interpret Docker's wrapper (the log, stream, and time fields) and can break on the actual log content inside.
How do you detect a suspicious interactive shell in a container with Wazuh?
When someone runs docker exec to open a shell, Wazuh captures exec_create and exec_start events; the latter triggers rule 87907 at level 5. You can add a custom rule (id 100110) on the manager that raises it to level 12 and maps it to MITRE ATT&CK T1609, matching shells like /bin/sh or /bin/bash, so an interactive access generates a high-severity alert.
Should FIM monitor the whole /var/lib/docker directory?
No. You should monitor Docker's configuration files like /etc/docker, the socket, and the binaries, but explicitly ignore /var/lib/docker/overlay2, /var/lib/docker/containers, and /var/lib/docker/tmp. Those data directories can grow to hundreds of GB and change constantly with overlay2 layers, which would generate thousands of false FIM alerts.
Conclusion
With this configuration you have full visibility over your Docker server: real-time API events, centralized logs from all containers, alerts on any change to the daemon configuration, and immediate detection of suspicious interactive access. All without any agent inside the containers.
If you also add Trivy and the SCA Docker CIS policy, you cover image vulnerabilities and misconfigurations too, before they reach production.
Have Docker in production and want to review how Wazuh is configured in your environment? Book a free consultation and we'll review it together.
Need to implement this solution? Book a free consultation here.