This page walks through the complete deployment of the Wazuh Docker Stack from repository clone to first Dashboard login. The preferred deployment path uses the includedDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/rsol9000-01/wazuh/llms.txt
Use this file to discover all available pages before exploring further.
wazuh-dev.sh helper script, which automates prerequisite validation, kernel tuning, password hashing, certificate generation, and the final docker compose up in a single command. By the end of this guide, all four services — Indexer, Manager, Dashboard, and Agent — will be running and the Dashboard will be accessible over HTTPS on port 6443.
Deployment Steps
Clone the Repository
Clone the Wazuh Docker Stack repository and change into the project directory:All subsequent commands in this guide assume you are running from the repository root.
Configure Environment Variables
Copy the example environment file to The following variables must be reviewed and set before deployment:
The
.env and edit it with your deployment-specific values:| Variable | Default (.env.example) | Description |
|---|---|---|
INDEXER_USERNAME | admin | Username for the Wazuh Indexer (OpenSearch) API — used by the Manager and Dashboard to authenticate |
INDEXER_PASSWORD | _S1M0V1L48_T7G_2025_.*- | Password for the Indexer API user — change this in production |
API_USERNAME | wazuh-wui | Username for the Wazuh Manager REST API — consumed by the Dashboard |
API_PASSWORD | MyS3cr37P450r.*- | Password for the Manager REST API user — must not contain a $ character |
DASHBOARD_USERNAME | kibanaserver | Username for the Dashboard’s Indexer connection |
DASHBOARD_PASSWORD | kibanaserver | Password for the Dashboard’s Indexer connection — change this in production |
MANAGER_SERVER | 10.20.203.5 | IP address or hostname of the Wazuh Manager, used by external agents for enrollment |
NETWORK | net_00 | Name of the Docker internal network created for inter-service communication |
SUBNET | 10.60.60.0/24 | CIDR block for the Docker internal network |
GATEWAY | 10.60.60.1 | Gateway address for the Docker internal network |
PROJECT_ROOT | . | Path to the repository root used as the Compose project base directory |
NGINX_IMAGE | nginx:1.29.3-alpine | NGINX image tag — used when a reverse proxy is added in front of the Dashboard |
GENERATE_INDEXER_FILE | generate-indexer-certs.yml | Filename of the Compose override used to run the certificate generator container |
TZ | (not set) | POSIX timezone string (e.g., Europe/Madrid) injected into all containers for consistent log timestamps |
DOCKER_GID | (auto-detected) | Docker group ID on the host — automatically resolved by wazuh-dev.sh via getent group docker; set manually if deploying without the script |
LOCAL_AGENT_HOSTNAME | (host FQDN) | Hostname assigned to the wazuh.agent container; if set to localhost, the script substitutes the actual host FQDN using hostname -f |
MY_USERNAME | (not set) | Username for a custom administrative user to be created or updated in the Indexer’s internal_users.yml |
MY_PASSWORD | (not set) | Password for the custom administrative user; the script hashes this using the Indexer’s hash.sh tool |
wazuh-dev.sh script reads API_USERNAME, API_PASSWORD, MY_USERNAME, and MY_PASSWORD directly from .env at runtime to patch configuration files and hash passwords before starting the stack.Generate TLS Certificates
Generate the mutual TLS certificates required for secure communication between the Indexer, Manager, and Dashboard:This command runs the
wazuh/wazuh-certs-generator:0.0.3 container, which reads the node definitions from config/certs.yml and outputs the following certificate files into ./config/wazuh_indexer_ssl_certs/:root-ca.pem(shared CA for Indexer and Dashboard)root-ca-manager.pem(CA copy for the Manager’s Filebeat)wazuh.indexer.pem/wazuh.indexer-key.pemwazuh.manager.pem/wazuh.manager-key.pemwazuh.dashboard.pem/wazuh.dashboard-key.pemadmin.pem/admin-key.pem
docker compose up.Deploy with wazuh-dev.sh
Launch the full stack using the deployment helper script:The script performs the following steps in order:
- Detects the Docker GID — resolves the host
dockergroup ID viagetent group dockerand exports it asDOCKER_GIDso the agent container can access/var/run/docker.sock - Sets
vm.max_map_count— checks the current value; if it is below262144, runssysctl -w vm.max_map_count=262144automatically - Validates dependencies — confirms that
dockerandcurlare available on the host PATH - Validates required files — checks for
.env,docker-compose.yml,config/wazuh_indexer/internal_users.yml, andgenerate-indexer-certs.yml - Resolves the agent hostname — reads
LOCAL_AGENT_HOSTNAMEfrom.env; substitutes the real FQDN if the value islocalhost - Patches API credentials — updates
config/wazuh_dashboard/wazuh.ymlwith theAPI_USERNAMEandAPI_PASSWORDvalues from.env - Hashes the custom user password — runs
wazuh/wazuh-indexer:4.14.5to executehash.shagainstMY_PASSWORDand writes the bcrypt hash intointernal_users.yml - Generates TLS certificates — runs
docker compose -f generate-indexer-certs.yml run --rm generator - Starts the stack — executes
docker compose -f docker-compose.yml --env-file .env up -d
server argument selects docker-compose.yml (the full four-service stack). Use agent to deploy only the agent via agent/docker-compose-agent.yml. The second argument controls the Compose action and defaults to up when omitted; all supported actions are listed below.| Action | Command | Description |
|---|---|---|
up | bash scripts/wazuh-dev.sh server up | Start the stack in detached mode (default) |
down | bash scripts/wazuh-dev.sh server down | Stop and remove all containers |
logs | bash scripts/wazuh-dev.sh server logs | Tail logs for all services |
ps | bash scripts/wazuh-dev.sh server ps | Show container status |
restart | bash scripts/wazuh-dev.sh server restart | Restart all running containers |
Verify the Stack
Confirm all containers are running and healthy:All four services — Look for Filebeat successfully connecting to the Indexer and the Manager API becoming available on port 55000. The Indexer health check runs every 30 seconds with up to 3 retries; the Manager and Dashboard health checks run every 10 seconds with up to 10 retries.
wazuh.indexer, wazuh.manager, wazuh.dashboard, and wazuh.agent — should show a status of running. Follow the Manager logs to watch the initial startup sequence:Access the Dashboard
Open a browser and navigate to the Dashboard on the secure HTTPS port:Log in with the credentials set in your
.env file:- Username: the value of
DASHBOARD_USERNAME(default:kibanaserver) - Password: the value of
DASHBOARD_PASSWORD(default:kibanaserver)
wazuh.agent has enrolled and is actively sending events to the Manager. The Dashboard communicates with the Manager API at https://wazuh.manager:55000 using the API_USERNAME and API_PASSWORD credentials.Quick Health Check
Once the stack is up, use the following commands to verify each service is responding:SecretPassword with the value of INDEXER_PASSWORD from your .env file. The -k flag bypasses TLS certificate validation for the self-signed certificates generated in Step 3.