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_count set 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

  1. Wait 30–60 seconds for all services to initialize. DataNode needs to complete its bootstrap before Graylog becomes responsive.
  2. Open http://your-server-ip:9000 in your browser.
  3. On first launch, Graylog displays a preflight check page showing DataNode status. Once DataNode is connected, click Resume Setup.
  4. Log in with username admin and the password you hashed for GRAYLOG_ROOT_PASSWORD_SHA2.
  5. Navigate to System → Inputs to configure log sources.

Create Your First Input

To start receiving logs, create a GELF UDP input:

  1. Go to System → Inputs
  2. Select GELF UDP from the dropdown and click Launch new input
  3. Set a title (e.g., “GELF UDP”) and leave the bind address as 0.0.0.0 port 12201
  4. 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:

  1. Create a Syslog UDP input on port 5140
  2. 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:

  1. Go to System → Indices
  2. Edit the Default index set
  3. Set rotation strategy (by time, size, or message count)
  4. 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:

  1. Go to System → Pipelines → Create Pipeline
  2. 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:

  1. Go to Alerts → Definitions → Create Event Definition
  2. Set a filter condition (e.g., level:3 for error logs)
  3. Add a notification (email, Slack webhook, or HTTP callback)

LDAP/Active Directory Integration

For multi-user environments:

  1. Go to System → Authentication → Configure LDAP
  2. Enter your LDAP server URI, bind DN, and search base
  3. 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.

Reverse Proxy Setup

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

Backup Strategy

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.

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

ComponentRAMCPUDisk
Graylog server1 GBLow-moderate500 MB + journal
DataNode1–4 GBModerateDepends on log retention
MongoDB512 MBLow1–5 GB (metadata)
Total (minimum)2.5 GB2 cores20 GB
Total (recommended)4–8 GB4 cores50+ 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.

Comments