How to Self-Host Graylog with Docker Compose
What Is Graylog?
Graylog is a centralized log management platform that collects, indexes, and analyzes log data from servers, containers, and network devices. It accepts logs via Syslog, GELF (Graylog Extended Log Format), Beats, and raw TCP/UDP — then lets you search, filter, and alert on them through a web interface. Think of it as a self-hosted Splunk or Datadog Logs without the per-GB pricing. Official site
Prerequisites
- A Linux server (Ubuntu 22.04+ recommended)
- Docker and Docker Compose installed (guide)
- 4 GB of RAM minimum (8 GB recommended for production)
- 20 GB of free disk space (more depending on log volume and retention)
- A domain name (optional, for remote access)
vm.max_map_countset to 262144 on the host (required for DataNode)
Set vm.max_map_count
DataNode requires elevated memory map limits. Set this before starting:
# Temporary (resets on reboot)
sudo sysctl -w vm.max_map_count=262144
# Permanent
echo "vm.max_map_count=262144" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
Docker Compose Configuration
Graylog 7.0+ uses DataNode as its integrated search backend, replacing the previous requirement for a standalone Elasticsearch or OpenSearch instance. The stack has three services: MongoDB, DataNode, and Graylog.
Create a docker-compose.yml file:
services:
mongodb:
image: mongo:7.0.17
container_name: graylog-mongodb
restart: unless-stopped
volumes:
- mongodb_data:/data/db
- mongodb_config:/data/configdb
networks:
- graylog
datanode:
image: graylog/graylog-datanode:7.0.5
container_name: graylog-datanode
restart: unless-stopped
hostname: datanode
environment:
GRAYLOG_DATANODE_NODE_ID_FILE: "/var/lib/graylog-datanode/node-id"
# Must match GRAYLOG_PASSWORD_SECRET on the Graylog server
GRAYLOG_DATANODE_PASSWORD_SECRET: "${GRAYLOG_PASSWORD_SECRET}"
GRAYLOG_DATANODE_MONGODB_URI: "mongodb://mongodb:27017/graylog"
ulimits:
memlock:
hard: -1
soft: -1
nofile:
soft: 65536
hard: 65536
volumes:
- datanode_data:/var/lib/graylog-datanode
networks:
- graylog
depends_on:
- mongodb
graylog:
image: graylog/graylog:7.0.5
container_name: graylog
restart: unless-stopped
environment:
# REQUIRED: Minimum 16 characters. Generate with: pwgen -N 1 -s 96
GRAYLOG_PASSWORD_SECRET: "${GRAYLOG_PASSWORD_SECRET}"
# REQUIRED: SHA-256 hash of your admin password
# Generate with: echo -n "YourPassword" | sha256sum | cut -d' ' -f1
GRAYLOG_ROOT_PASSWORD_SHA2: "${GRAYLOG_ROOT_PASSWORD_SHA2}"
# URL where the browser can reach Graylog (include trailing slash)
GRAYLOG_HTTP_EXTERNAL_URI: "http://127.0.0.1:9000/"
GRAYLOG_MONGODB_URI: "mongodb://mongodb:27017/graylog"
GRAYLOG_NODE_ID_FILE: "/usr/share/graylog/data/node-id"
# Timezone for log display
GRAYLOG_ROOT_TIMEZONE: "UTC"
entrypoint: /usr/bin/tini -- wait-for-it mongodb:27017 -- /docker-entrypoint.sh
ports:
# Web UI and REST API
- "9000:9000"
# Syslog TCP
- "5140:5140"
# Syslog UDP
- "5140:5140/udp"
# GELF TCP
- "12201:12201"
# GELF UDP
- "12201:12201/udp"
# Beats input
- "5044:5044"
volumes:
- graylog_data:/usr/share/graylog/data
networks:
- graylog
depends_on:
- mongodb
- datanode
networks:
graylog:
driver: bridge
volumes:
mongodb_data:
mongodb_config:
datanode_data:
graylog_data:
Create a .env file alongside:
# Generate a password secret (minimum 16 characters, 96 recommended):
# pwgen -N 1 -s 96
GRAYLOG_PASSWORD_SECRET=CHANGE_ME_generate_a_long_random_string_at_least_96_characters
# Generate the SHA-256 hash of your desired admin password:
# echo -n "YourSecurePassword" | sha256sum | cut -d' ' -f1
GRAYLOG_ROOT_PASSWORD_SHA2=CHANGE_ME_put_sha256_hash_here
Generate the required values:
# Generate password secret
pwgen -N 1 -s 96
# Generate admin password hash (replace YourSecurePassword)
echo -n "YourSecurePassword" | sha256sum | cut -d' ' -f1
Start the stack:
docker compose up -d
Initial Setup
- Wait 30–60 seconds for all services to initialize. DataNode needs to complete its bootstrap before Graylog becomes responsive.
- Open
http://your-server-ip:9000in your browser. - On first launch, Graylog displays a preflight check page showing DataNode status. Once DataNode is connected, click Resume Setup.
- Log in with username
adminand the password you hashed forGRAYLOG_ROOT_PASSWORD_SHA2. - Navigate to System → Inputs to configure log sources.
Create Your First Input
To start receiving logs, create a GELF UDP input:
- Go to System → Inputs
- Select GELF UDP from the dropdown and click Launch new input
- Set a title (e.g., “GELF UDP”) and leave the bind address as
0.0.0.0port12201 - Click Launch
Now send a test log:
echo '{"version":"1.1","host":"test","short_message":"Hello Graylog","level":6}' \
| nc -u -w1 127.0.0.1 12201
The message should appear in the Search page within seconds.
Configuration
Docker Log Driver
Send all Docker container logs to Graylog by configuring Docker’s GELF log driver:
{
"log-driver": "gelf",
"log-opts": {
"gelf-address": "udp://localhost:12201",
"tag": "{{.Name}}"
}
}
Add this to /etc/docker/daemon.json and restart Docker. All containers will forward their stdout/stderr to Graylog.
Or configure per-container in docker-compose.yml:
services:
myapp:
image: myapp:latest
logging:
driver: gelf
options:
gelf-address: "udp://graylog-host:12201"
tag: "myapp"
Syslog Input
For syslog from Linux servers and network devices:
- Create a Syslog UDP input on port
5140 - Configure rsyslog on remote hosts:
# /etc/rsyslog.d/90-graylog.conf
*.* @graylog-host:5140;RSYSLOG_SyslogProtocol23Format
Index Rotation and Retention
Control how long logs are stored:
- Go to System → Indices
- Edit the Default index set
- Set rotation strategy (by time, size, or message count)
- Set retention strategy (delete, close, or archive old indices)
A reasonable starting point: rotate every 1 GB, retain 10 indices (10 GB total).
Advanced Configuration
Extractors and Processing Pipelines
Graylog’s processing pipelines parse and enrich log messages. Example pipeline rule to extract HTTP status codes from nginx access logs:
- Go to System → Pipelines → Create Pipeline
- Add a rule:
rule "extract_http_status"
when
has_field("message") AND contains(to_string($message.message), "HTTP/")
then
let msg = to_string($message.message);
let status = regex("HTTP/\\d\\.\\d\" (\\d{3})", msg);
set_field("http_status", status["0"]);
end
Alerting
Create alerts for critical events:
- Go to Alerts → Definitions → Create Event Definition
- Set a filter condition (e.g.,
level:3for error logs) - Add a notification (email, Slack webhook, or HTTP callback)
LDAP/Active Directory Integration
For multi-user environments:
- Go to System → Authentication → Configure LDAP
- Enter your LDAP server URI, bind DN, and search base
- Map LDAP groups to Graylog roles
Reverse Proxy
Nginx configuration for Graylog behind a reverse proxy:
server {
listen 443 ssl;
server_name graylog.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/graylog.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/graylog.yourdomain.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:9000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Graylog-Server-URL https://graylog.yourdomain.com/;
}
}
Update GRAYLOG_HTTP_EXTERNAL_URI to match your public URL when using a reverse proxy.
Backup
Back up these components:
- MongoDB — stores configuration, users, dashboards, and alert definitions
- DataNode volume — stores indexed log data
- Graylog data volume — stores the message journal and node ID
# Backup MongoDB
docker exec graylog-mongodb mongodump --out /dump
docker cp graylog-mongodb:/dump ./graylog-mongodb-backup
# Backup volumes (stop services first for consistency)
docker compose stop
tar -czf graylog-backup.tar.gz \
/var/lib/docker/volumes/graylog_mongodb_data \
/var/lib/docker/volumes/graylog_datanode_data \
/var/lib/docker/volumes/graylog_graylog_data
docker compose start
Troubleshooting
DataNode fails to start
Symptom: DataNode container exits immediately or restarts in a loop.
Fix: Check vm.max_map_count:
sysctl vm.max_map_count
# Must show 262144 or higher
sudo sysctl -w vm.max_map_count=262144
Graylog shows “No DataNode connected”
Symptom: Preflight check page never completes.
Fix: Verify GRAYLOG_DATANODE_PASSWORD_SECRET matches GRAYLOG_PASSWORD_SECRET exactly. Both services must share the same secret. Also check that the MongoDB URI is identical on both services.
Logs not appearing in search
Symptom: Messages sent to GELF/Syslog input don’t show in the Search page.
Fix: Verify the input is running (green indicator in System → Inputs). Check the input’s bind address — 0.0.0.0 accepts connections from all interfaces. If using Docker’s GELF log driver, ensure the container can reach the Graylog host IP (not localhost if Graylog is also in Docker).
High memory usage
Symptom: DataNode consumes excessive RAM. Fix: DataNode (based on OpenSearch) defaults to using 50% of available RAM for its JVM heap. Limit it with:
environment:
OPENSEARCH_JAVA_OPTS: "-Xms1g -Xmx1g"
Adjust based on your log volume — 1 GB heap handles moderate loads.
Admin password not working
Symptom: Cannot log in with admin account.
Fix: Regenerate the password hash. Ensure you’re using sha256sum, not md5sum. The input must not include a trailing newline:
# Correct (echo -n suppresses newline)
echo -n "MyPassword" | sha256sum | cut -d' ' -f1
Resource Requirements
| Component | RAM | CPU | Disk |
|---|---|---|---|
| Graylog server | 1 GB | Low-moderate | 500 MB + journal |
| DataNode | 1–4 GB | Moderate | Depends on log retention |
| MongoDB | 512 MB | Low | 1–5 GB (metadata) |
| Total (minimum) | 2.5 GB | 2 cores | 20 GB |
| Total (recommended) | 4–8 GB | 4 cores | 50+ GB |
Log storage scales linearly with ingestion rate and retention period. A server ingesting 1 GB of logs per day with 30-day retention needs ~30 GB of DataNode storage.
Verdict
Graylog is the best self-hosted log management platform for most infrastructure teams. The 7.0 DataNode architecture eliminates the complexity of managing a separate Elasticsearch/OpenSearch cluster, and the web UI makes searching, filtering, and alerting on logs accessible without learning a query DSL from scratch.
For small home labs, Graylog’s 2.5 GB minimum RAM requirement is steep — consider Loki with Grafana instead, which can run in under 512 MB. For anything beyond a handful of devices, Graylog’s structured approach to log management pays for itself quickly. The GELF protocol integrates natively with Docker, making container log centralization trivial.
FAQ
How does Graylog compare to the ELK/EFK stack?
Graylog replaces Elasticsearch + Logstash + Kibana with a single integrated platform. You get search, ingestion, and dashboards without configuring three separate tools. The trade-off is less flexibility — Kibana’s visualization engine is more powerful, and Logstash’s plugin ecosystem is larger.
Can I send logs from remote servers?
Yes. Use rsyslog (Syslog protocol), Filebeat (Beats protocol), or FluentBit/Fluentd (GELF output) on remote servers to ship logs to Graylog. All protocols are supported out of the box.
Is Graylog free for production use?
Graylog Open is free under the SSPL license. It includes all core features: log ingestion, search, dashboards, alerting, and RBAC. Graylog Enterprise and Cloud add features like archiving, audit logging, and multi-node Graylog server clustering.
How much storage do I need?
Estimate 1–2 GB of storage per GB of raw logs ingested (DataNode compresses and indexes). A moderate setup ingesting 5 GB/day with 14-day retention needs ~70–140 GB. Configure index rotation and retention policies to manage disk usage automatically.
Related
Get self-hosting tips in your inbox
Get the Docker Compose configs, hardware picks, and setup shortcuts we don't put in articles. Weekly. No spam.
Comments