VictoriaLogs + Alloy Docker Deployment
If MongoDB log rotation is not configured, first see MongoDB Log Rotation. The Docker image includes Grafana and the slow query dashboard.
Prerequisites
- Installed
docker - Installed
docker-compose
Deploy VictoriaLogs + Grafana
-
Download the image
- Servers with Internet Access
- Servers without Internet Access
docker pull registry.cn-hangzhou.aliyuncs.com/hap-mdy/hap-monitor-victorialogs-grafana-amd64:1.1.0# Download the offline image archive and upload it to the serverhttps://pdpublic.mingdao.com/private-deployment/offline/common/hap-monitor-victorialogs-grafana-amd64_1.1.0.tar.gz# Load the offline image using docker load -idocker load -i hap-monitor-victorialogs-grafana-amd64_1.1.0.tar.gz -
Create configuration and data directories
mkdir -p /usr/local/victoria-logs-linuxmkdir -p /data/victorialogsmkdir -p /data/grafana -
Create the configuration file
cat > /usr/local/victoria-logs-linux/hap-monitor-logs.yaml << 'EOF'services:hap-monitor-logs:image: registry.cn-hangzhou.aliyuncs.com/hap-mdy/hap-monitor-victorialogs-grafana-amd64:1.1.0container_name: hap-monitor-logsrestart: alwaysports:# VictoriaLogs API- "9428:9428"# Grafana- "3000:3000"# Persistencevolumes:# VictoriaLogs data directory- /data/victorialogs:/data/victorialogs# Grafana data directory- /data/grafana:/usr/local/grafana/dataenvironment:# VictoriaLogs HTTP Basic Auth; use the same credentials for Alloy (change in production)MONITOR_AUTH_USERNAME: myuserMONITOR_AUTH_PASSWORD: mypassword# External URL (change for reverse proxy or subpath access)GRAFANA_SERVER_ROOT_URL: "%(protocol)s://%(domain)s:%(http_port)s/privatedeploy/mdy/monitor/victorialogs-grafana/"# Match GRAFANA_SERVER_ROOT_URL: true for a subpath, false for root accessGRAFANA_SERVER_SERVE_FROM_SUB_PATH: "true"# Grafana administrator credentials (change in production)GRAFANA_ADMIN_USER: adminGRAFANA_ADMIN_PASSWORD: admin@123456# Resource limitsmem_limit: 3gmemswap_limit: 3gcpus: 2.0ulimits:nofile:soft: 102400hard: 102400# Logslogging:driver: json-fileoptions:max-size: "100m"max-file: "3"EOF
Start and Stop
# Start
docker-compose -f /usr/local/victoria-logs-linux/hap-monitor-logs.yaml up -d
# Stop
docker-compose -f /usr/local/victoria-logs-linux/hap-monitor-logs.yaml down
# Check status
docker-compose -f /usr/local/victoria-logs-linux/hap-monitor-logs.yaml ps
# View logs
docker-compose -f /usr/local/victoria-logs-linux/hap-monitor-logs.yaml logs
Variable Reference
All variables can be overridden through Compose environment. Variables marked as omitted from Compose use startup script defaults and need explicit configuration only when changing the default behavior.
Authentication and Service Modes
| Variable | Compose Status | Default | Description | Supported Version |
|---|---|---|---|---|
MONITOR_AUTH_USERNAME / MONITOR_AUTH_PASSWORD | Included; change in production | myuser / mypassword | VictoriaLogs external Basic Auth credentials, also used by Alloy and the Grafana datasource. | 1.1.0+ |
MONITOR_SERVICES | Omitted from Compose | all | all starts VictoriaLogs and Grafana; victorialogs or grafana starts one service. Unset or empty defaults to all. | 1.1.0+ |
Grafana
| Variable | Compose Status | Default | Description | Supported Version |
|---|---|---|---|---|
GRAFANA_ADMIN_USER / GRAFANA_ADMIN_PASSWORD | Included; must change | admin / admin@123456 | Grafana administrator credentials. | 1.0.0+ |
GRAFANA_SERVER_ROOT_URL | Included; adjust for deployment | %(protocol)s://%(domain)s:%(http_port)s/privatedeploy/mdy/monitor/victorialogs-grafana/ | External Grafana URL. Set the actual address for a reverse proxy or subpath. | 1.0.0+ |
GRAFANA_SERVER_SERVE_FROM_SUB_PATH | Included; match ROOT_URL | true | Set to true when GRAFANA_SERVER_ROOT_URL has a subpath, or false for root access. | 1.0.0+ |
GRAFANA_ALLOW_UNSIGNED_PLUGINS | Omitted from Compose | victoriametrics-logs-datasource | The startup script allows the current plugin by default; override if its name changes. | 1.0.0+ |
GRAFANA_VL_DATASOURCE_URL | Omitted from Compose | http://127.0.0.1:9428 | VictoriaLogs URL used by Grafana. Set an external address when using MONITOR_SERVICES=grafana. | 1.0.0+ |
Runtime and Storage
| Variable | Compose Status | Default | Description | Supported Version |
|---|---|---|---|---|
TZ | Omitted from Compose | Asia/Shanghai | Container time zone; override the startup script default when needed. | 1.0.0+ |
GOMEMLIMIT | Omitted from Compose | 2GiB | Go runtime memory limit; do not exceed Compose mem_limit. | 1.0.0+ |
VL_STORAGE_DATA_PATH | Omitted from Compose | /data/victorialogs | VictoriaLogs data directory. Update the Compose volumes mapping when changing it. | 1.0.0+ |
VL_HTTP_LISTEN_ADDR | Omitted from Compose | :9428 | VictoriaLogs HTTP listen address. Update ports and the Alloy write address if the port changes. | 1.0.0+ |
Retention, Concurrency, and Queries
| Variable | Compose Status | Default | Description | Supported Version |
|---|---|---|---|---|
VL_RETENTION_PERIOD | Omitted from Compose | 30d | Maximum log retention period; older data is removed. | 1.0.0+ |
VL_RETENTION_DISK_USAGE | Omitted from Compose | 50GiB | Maximum disk space for the VictoriaLogs data directory; applies alongside time-based retention. | 1.0.0+ |
VL_MEMORY_ALLOWED_PERCENT | Omitted from Compose | 2 | VictoriaLogs memory usage percentage. | 1.0.0+ |
VL_MAX_CONCURRENT_INSERTS | Omitted from Compose | 4 | Maximum concurrent ingestion requests; increasing it raises CPU, memory, and disk pressure. | 1.0.0+ |
VL_MAX_CONCURRENT_REQUESTS | Omitted from Compose | 2 | Maximum concurrent query requests; excess requests enter a queue. | 1.0.0+ |
VL_SEARCH_MAX_QUERY_DURATION | Omitted from Compose | 30s | Maximum execution time per query; queries terminate on timeout. | 1.0.0+ |
VL_SEARCH_MAX_QUERY_TIMERANGE | Omitted from Compose | 24h | Maximum allowed time range per query. | 1.0.0+ |
VL_SEARCH_MAX_QUEUE_DURATION | Omitted from Compose | 5s | Maximum query wait time in the concurrency queue. | 1.0.0+ |
VL_SEARCH_LOG_SLOW_QUERY_DURATION | Omitted from Compose | 3s | Log slow queries when execution reaches this duration. | 1.0.0+ |
Log Ingestion and Logging Level
| Variable | Compose Status | Default | Description | Supported Version |
|---|---|---|---|---|
VL_INSERT_MAX_LINE_SIZE_BYTES | Omitted from Compose | 262144 | Maximum size per log entry, 256 KiB; larger entries are rejected. | 1.0.0+ |
VL_INSERT_MAX_FIELDS_PER_LINE | Omitted from Compose | 300 | Maximum number of fields per log entry. | 1.0.0+ |
VL_SYSLOG_EXTRA_FIELDS_TCP | Omitted from Compose | {"job":"rsyslog"} | JSON fields added to TCP Syslog ingestion. | 1.0.0+ |
VL_LOGGER_LEVEL | Omitted from Compose | INFO | VictoriaLogs logging level. Common values: DEBUG, INFO, WARN, ERROR. | 1.0.0+ |
Service modes:
MONITOR_SERVICES=all: Start VictoriaLogs and Grafana for the default deployment.MONITOR_SERVICES=grafana: Start only Grafana; setGRAFANA_VL_DATASOURCE_URLto the external VictoriaLogs address.MONITOR_SERVICES=victorialogs: Start only VictoriaLogs, without Grafana.
Deploy the Alloy Log Collector
Deploy Alloy on every MongoDB node to read its local mongodb.log and send slow query logs to VictoriaLogs. Each node collects its own logs.
Notes:
VICTORIALOGS_IP: VictoriaLogs service address; adjust for your environment.MONITOR_AUTH_USERNAME: VictoriaLogs authentication username; adjust for your environment.MONITOR_AUTH_PASSWORD: VictoriaLogs authentication password; adjust for your environment.VICTORIALOGS_PORT: VictoriaLogs service port; default is 9428.MONGODB_SERVER_IP: MongoDB server IP, used to generate the uniquemongodb-<IP>identifier.- In
/data/logs/mongodb/mongodb.log:/data/logs/mongodb/mongodb.log:ro, change the path before the colon to the actual MongoDB log file on the server.
-
Download the Alloy image
- Servers with Internet Access
- Servers without Internet Access
docker pull registry.cn-hangzhou.aliyuncs.com/hap-mdy/hap-alloy-vlogs-mongodb-amd64:1.0.0# Download the offline image archive and upload it to the serverhttps://pdpublic.mingdao.com/private-deployment/offline/common/hap-alloy-vlogs-mongodb-amd64_1.0.0.tar.gz# Load the offline image using docker load -idocker load -i hap-alloy-vlogs-mongodb-amd64_1.0.0.tar.gz -
Create configuration and data directories
mkdir -p /usr/local/alloy-vlogs/ /data/alloy-data -
Create the Alloy configuration file
cat > /usr/local/alloy-vlogs/alloy-vlogs-mongodb.yaml << 'EOF'services:alloy-vlogs-mongodb:image: registry.cn-hangzhou.aliyuncs.com/hap-mdy/hap-alloy-vlogs-mongodb-amd64:1.0.0container_name: alloy-vlogs-mongodbrestart: alwaysenvironment:# VictoriaLogs addressVICTORIALOGS_IP: 10.206.0.5# VictoriaLogs portVICTORIALOGS_PORT: 9428# Use VictoriaLogs credentialsMONITOR_AUTH_USERNAME: myuserMONITOR_AUTH_PASSWORD: mypassword# MongoDB server IP, used to generate a unique mongodb-<IP> identifierMONGODB_SERVER_IP: 10.206.0.8volumes:- /data/logs/mongodb/mongodb.log:/data/logs/mongodb/mongodb.log:ro- /etc/hostname:/host/etc/hostname:ro- /etc/machine-id:/host/etc/machine-id:ro- /data/alloy-data:/usr/local/alloy-vlogs/datadeploy:resources:limits:cpus: '1'memory: 1GEOF
Start and Stop
# Start
docker-compose -f /usr/local/alloy-vlogs/alloy-vlogs-mongodb.yaml up -d
# Stop
docker-compose -f /usr/local/alloy-vlogs/alloy-vlogs-mongodb.yaml down
# Check status
docker-compose -f /usr/local/alloy-vlogs/alloy-vlogs-mongodb.yaml ps
# View logs
docker-compose -f /usr/local/alloy-vlogs/alloy-vlogs-mongodb.yaml logs
Variable Reference
Confirm these five items during deployment:
| Variable | Configuration | Default | Example | Purpose |
|---|---|---|---|---|
VICTORIALOGS_IP | Check | hap-monitor-logs | 10.206.0.5 | VictoriaLogs address; use the service name when in the same Compose project. |
VICTORIALOGS_PORT | Check | 9428 | 9428 | VictoriaLogs HTTP port. |
MONITOR_AUTH_USERNAME | Required | None | myuser | VictoriaLogs authentication username. |
MONITOR_AUTH_PASSWORD | Required | None | mypassword | VictoriaLogs authentication password; change it in production. |
MONGODB_SERVER_IP | Recommended | Generated using machine-id | 10.206.0.8 | Generates a unique job name, such as mongodb-10.206.0.8. |
- If
MONGODB_SERVER_IPorMONGODB_INSTANCE_IDis not set, Compose mounts the host/etc/machine-idread-only and generates a name such asmongodb-a1b2c3d4e5f6. After cloning a VM or server, ensure itsmachine-idhas been regenerated.
Optional variables and defaults:
| Variable | Required | Default | Purpose |
|---|---|---|---|
MONGODB_LOG_PATH | No | /data/logs/mongodb/mongodb.log | MongoDB log path inside the container; must match the mount target. |
MONGODB_JOB_NAME | No | Automatically generated | Manually specify a unique job name; the mongodb- prefix is recommended. |
MONGODB_INSTANCE_ID | No | Empty | Stable instance identifier when no server IP is available. |
MONGODB_HOSTNAME | No | Automatically read host hostname | For display only, not a unique identifier. |
VICTORIALOGS_PUSH_URL | Do not set | Automatically generated | Address used by Alloy to write to VictoriaLogs. |
Variable precedence: host environment > --env-file > Compose file > startup script defaults.
When server IP, instance ID, and job name are unset, the startup script uses the first 12 characters of host /etc/machine-id to generate a unique job name.
Built-in Alloy settings:
| Item | Value | Description |
|---|---|---|
| Configuration file | /usr/local/alloy-vlogs/config.alloy | Reads MongoDB logs, filters slow queries, and writes to VictoriaLogs. |
| Executable path | /usr/local/alloy-vlogs/bin/alloy | Alloy executable path. |
| HTTP listen address | 0.0.0.0:9081 | Management and health-check port; not exposed by Compose by default. |
| State directory | /usr/local/alloy-vlogs/data | Stores file read positions; mount a host directory for persistence. |
Generate Test Slow Query Logs
- These commands test the collection pipeline by appending 15 MongoDB slow query entries exceeding 1000 ms to
/data/logs/mongodb/mongodb.log. Table IDs, database names, UUIDs, and signatures are sanitized examples; each entry uses the current UTC time. - Change
/data/logs/mongodb/mongodb.logto the actual log path.
- Linux Basic Commands
- Python
Uses Bash, date, and cat without Python. It generates three random table names and writes 15 entries, five per table, at one-second intervals so each ns has multiple timestamps.
Click to view the Linux test script
sudo bash <<'BASH'
set -eu
LOG_FILE=/data/logs/mongodb/mongodb.log
mkdir -p "$(dirname "$LOG_FILE")"
touch "$LOG_FILE"
DURATIONS=(1250 2387 43146 1680 9021 1380 2670 5120 1940 8030 1560 3290 6840 2210 9470)
TABLE_IDS=(
"ws$(od -An -N12 -tx1 /dev/urandom | tr -d ' \n')"
"ws$(od -An -N12 -tx1 /dev/urandom | tr -d ' \n')"
"ws$(od -An -N12 -tx1 /dev/urandom | tr -d ' \n')"
)
for i in "${!DURATIONS[@]}"; do
now="$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)"
epoch="$(date +%s)"
uuid="$(cat /proc/sys/kernel/random/uuid)"
table_id="${TABLE_IDS[$((i % ${#TABLE_IDS[@]}))]}"
duration="${DURATIONS[$i]}"
cat >> "$LOG_FILE" <<EOF
{"t":{"\$date":"$now"},"s":"I","c":"COMMAND","id":51803,"ctx":"conn$((100+i+1))","msg":"Slow query","attr":{"command":{"\$clusterTime":{"clusterTime":{"\$timestamp":{"i":$((i+1)),"t":$epoch}},"signature":{"hash":{"\$binary":{"base64":"REDACTED","subType":"0"}},"keyId":1}},"\$db":"example_db","find":"$table_id","filter":{"status":1,"sys_lock":{"\$ne":true}},"limit":2000,"lsid":{"id":{"\$uuid":"$uuid"}},"sort":{"utime":-1}},"cursorExhausted":true,"docsExamined":1010000,"durationMillis":$duration,"keysExamined":1010000,"nreturned":0,"ns":"example_db.$table_id","numYields":4095,"planCacheKey":"demo$i","planSummary":"IXSCAN { utime: 1 }","protocol":"op_msg","queryHash":"demo_hash_$i","reslen":249,"type":"command"}}
EOF
sleep 1
done
echo "Wrote ${#DURATIONS[@]} test slow query entries: $LOG_FILE"
BASH
The Python version also generates three random table names and writes 15 entries, five per table, at one-second intervals so each ns has multiple timestamps.
Click to view the Python test script
sudo python3 - <<'PY'
import json
import os
import secrets
import time
import uuid
from datetime import datetime, timezone
path = "/data/logs/mongodb/mongodb.log"
os.makedirs(os.path.dirname(path), exist_ok=True)
durations = [1250, 2387, 43146, 1680, 9021, 1380, 2670, 5120, 1940, 8030, 1560, 3290, 6840, 2210, 9470]
tables = ["ws" + secrets.token_hex(12) for _ in range(3)]
with open(path, "a", encoding="utf-8") as stream:
for index, duration in enumerate(durations, 1):
table = tables[(index - 1) % len(tables)]
now = datetime.now(timezone.utc)
epoch = int(now.timestamp())
log = {
"t": {"$date": now.isoformat(timespec="milliseconds").replace("+00:00", "Z")},
"s": "I",
"c": "COMMAND",
"id": 51803,
"ctx": f"conn{100 + index}",
"msg": "Slow query",
"attr": {
"command": {
"$clusterTime": {
"clusterTime": {"$timestamp": {"i": index, "t": epoch}},
"signature": {
"hash": {"$binary": {"base64": "REDACTED", "subType": "0"}},
"keyId": 1,
},
},
"$db": "example_db",
"find": table,
"filter": {"status": 1, "sys_lock": {"$ne": True}},
"limit": 2000,
"lsid": {"id": {"$uuid": str(uuid.uuid4())}},
"sort": {"utime": -1},
},
"cursorExhausted": True,
"docsExamined": 1010000,
"durationMillis": duration,
"keysExamined": 1010000,
"nreturned": 0,
"ns": f"example_db.{table}",
"numYields": 4095,
"planCacheKey": f"demo{index}",
"planSummary": "IXSCAN { utime: 1 }",
"protocol": "op_msg",
"queryHash": f"demo_hash_{index}",
"reslen": 249,
"type": "command",
},
}
stream.write(json.dumps(log, separators=(",", ":")) + "\n")
stream.flush()
time.sleep(1)
print(f"Wrote {len(durations)} test slow query entries: {path}")
PY
Verify the result:
tail -n 5 /data/logs/mongodb/mongodb.log | jq -r '[.t["$date"], .attr.ns, .attr.durationMillis] | @tsv'
Configure the Grafana Reverse Proxy
- Replace
192.168.1.10:3000with the actual Grafana address and port. - Replace
hap.domain.com:80with the external domain and port, and updateserver_name. - The proxy subpath must match
GRAFANA_SERVER_ROOT_URL.
upstream grafana {
server 192.168.1.10:3000;
}
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name hap.domain.com;
access_log /data/logs/weblogs/grafana.log main;
error_log /data/logs/weblogs/grafana.mingdao.net.error.log;
location /privatedeploy/mdy/monitor/victorialogs-grafana/ {
#allow 1.1.1.1;
#deny all;
proxy_hide_header X-Frame-Options;
proxy_set_header X-Frame-Options ALLOWALL;
proxy_set_header Host $http_host;
proxy_pass http://grafana;
proxy_redirect http://localhost:3000 http://hap.domain.com:80/privatedeploy/mdy/monitor/victorialogs-grafana;
}
location /privatedeploy/mdy/monitor/victorialogs-grafana/api/live {
rewrite ^/(.*) /$1 break;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $http_host;
proxy_pass http://grafana;
}
}
Access Notes
After configuring the proxy, access Grafana at:
http://hap.domain.com/privatedeploy/mdy/monitor/victorialogs-grafana
- Use the
GRAFANA_ADMIN_USERandGRAFANA_ADMIN_PASSWORDvalues from the configuration above. - Username: admin
- Password: admin@123456