Graylog on a VPS: Centralized Log Management with Full-Text Search and Alerting

Graylog on a VPS: Centralized Log Management with Full-Text Search and Alerting

Graylog is an open-source log management platform with a more operator-friendly interface than the ELK stack — it provides centralized log collection via GELF, syslog, and Beats, full-text search across all logs, stream-based routing, alerting on patterns, dashboards, and built-in user management. While Loki is lighter-weight, Graylog provides richer search capabilities and is better suited for teams that need to search logs extensively.

Step 1: Docker Compose Setup

<code">mkdir -p /opt/graylog && cd /opt/graylog
nano docker-compose.yml
<code">version: '3.8'

services:
  # MongoDB for Graylog metadata
  mongo:
    image: mongo:7.0
    restart: always
    volumes:
      - mongo_data:/data/db

  # OpenSearch for log storage
  opensearch:
    image: opensearchproject/opensearch:2.11.0
    restart: always
    environment:
      OPENSEARCH_JAVA_OPTS: "-Xms2g -Xmx2g"   # Adjust to your VPS RAM
      bootstrap.memory_lock: "true"
      discovery.type: single-node
      plugins.security.disabled: "true"        # Disable for internal-only
    ulimits:
      memlock:
        hard: -1
        soft: -1
    volumes:
      - opensearch_data:/usr/share/opensearch/data

  # Graylog server
  graylog:
    image: graylog/graylog:6.0
    restart: always
    depends_on:
      - mongo
      - opensearch
    ports:
      - "127.0.0.1:9000:9000"     # Web UI
      - "0.0.0.0:12201:12201/udp" # GELF UDP input (for log shipping)
      - "0.0.0.0:12201:12201/tcp" # GELF TCP input
      - "0.0.0.0:514:5140/udp"    # Syslog UDP (high port, map to 514)
    environment:
      GRAYLOG_NODE_ID_FILE: /usr/share/graylog/data/config/node-id
      GRAYLOG_HTTP_BIND_ADDRESS: "0.0.0.0:9000"
      GRAYLOG_HTTP_EXTERNAL_URI: "https://logs.yourdomain.com/"
      GRAYLOG_ELASTICSEARCH_HOSTS: "http://opensearch:9200"
      GRAYLOG_MONGODB_URI: "mongodb://mongo/graylog"
      GRAYLOG_PASSWORD_SECRET: ${GRAYLOG_SECRET}
      GRAYLOG_ROOT_PASSWORD_SHA2: ${GRAYLOG_ROOT_PASSWORD_SHA2}
    volumes:
      - graylog_data:/usr/share/graylog/data

volumes:
  mongo_data:
  opensearch_data:
  graylog_data:
<code"># Generate password hash:
echo -n "YourAdminPassword!" | sha256sum | cut -d" " -f1

cat > .env << 'EOF'
GRAYLOG_SECRET=$(openssl rand -hex 32)
GRAYLOG_ROOT_PASSWORD_SHA2=PASTE_SHA256_HASH_HERE
EOF
chmod 600 .env

docker compose up -d
# Wait 2-3 minutes for OpenSearch to initialize
docker compose logs -f graylog | grep "Graylog server up"

Step 2: Nginx Reverse Proxy

<code">sudo nano /etc/nginx/sites-available/graylog
<code">server {
    listen 443 ssl http2;
    server_name logs.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/logs.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/logs.yourdomain.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:9000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 300s;
    }
}
<code">sudo certbot --nginx -d logs.yourdomain.com
sudo systemctl reload nginx

Step 3: Configure Inputs

<code"># Graylog UI → System → Inputs → Launch new input

# GELF UDP Input (recommended — for application logs):
# Type: GELF UDP
# Title: GELF UDP
# Port: 12201
# Bind address: 0.0.0.0

# Syslog UDP Input (for OS-level logs):
# Type: Syslog UDP
# Title: Syslog
# Port: 5140  (mapped to 514 in docker-compose)
# Bind address: 0.0.0.0

Step 4: Ship Logs to Graylog

<code"># Method 1: rsyslog → Graylog (GELF over UDP)
sudo nano /etc/rsyslog.d/90-graylog.conf
<code">module(load="mmjsonparse")
template(name="GELF" type="list") {
    constant(value="{\"version\":\"1.1\",")
    constant(value="\"host\":\"")
    property(name="hostname")
    constant(value="\",\"short_message\":\"")
    property(name="msg" format="json")
    constant(value="\",\"timestamp\":")
    property(name="timegenerated" dateformat="unixtimestamp")
    constant(value=",\"level\":")
    property(name="syslogseverity")
    constant(value="}")
}
*.* action(type="omfwd" Target="YOUR_GRAYLOG_VPS_IP" Port="12201" Protocol="udp" Template="GELF")
<code">sudo systemctl restart rsyslog
<code"># Method 2: Application code → GELF (Python):
pip install graypy
import logging, graypy

logger = logging.getLogger('myapp')
handler = graypy.GELFUDPHandler('graylog.yourdomain.com', 12201)
logger.addHandler(handler)

logger.warning('High memory usage detected', extra={
    'service': 'api-server',
    'memory_percent': 87.5,
    'host': 'vps-01',
})

# Method 3: Node.js → GELF:
npm install winston-gelf-transporter
const { createLogger } = require('winston');
const GelfTransport = require('winston-gelf-transporter');
const logger = createLogger({
    transports: [new GelfTransport({ host: 'graylog.yourdomain.com', port: 12201 })]
});

Step 5: Search and Streams

<code"># Graylog search syntax (Lucene-based):
# Search all error logs:
level:<=3

# Find logs from specific service:
service:api-server AND level:<=4

# Full-text search:
"database connection failed"

# Time range + filter:
source:web-server AND short_message:/nginx.*502/

# Field-based queries:
http_status:500 AND NOT source:health-checker

# Streams = saved filters that route logs:
# System → Streams → Create Stream
# Rule: field "service" matches "api-server"
# Use streams for: team-specific log views, alert targets

Step 6: Configure Alerts

<code"># Alerts → Alert Definitions → Create

# Alert: High error rate
# Condition: Message count > 100 in 5 minutes
# Stream: API Server logs
# Filter: level:<=3 (errors only) # Action: Email team@yourdomain.com # Alert: Specific pattern (e.g., "OutOfMemoryError") # Condition: Message count > 1 in 1 minute
# Filter: message_text:"OutOfMemoryError"
# Action: PagerDuty or Slack webhook

Getting Started

Graylog with OpenSearch needs 4+ GB RAM — deploy on a dedicated 4 GB Ubuntu VPS at VPS.DO. OpenSearch’s JVM heap is the main consumer (set to 2 GB in the config above). For log retention, plan for 1–5 GB per day per server being monitored, depending on verbosity. SSD or NVMe storage is essential for OpenSearch performance — HDD-backed VPS instances make log search painfully slow.

Conclusion

Graylog provides team-friendly centralized log management with full-text search, stream-based routing, and pattern alerting. The GELF protocol makes it easy to ship structured logs from any application (Python, Node.js, Java, Go) alongside system syslog, with all logs searchable from one interface. For teams that find Loki’s label-based approach limiting and need richer full-text search capabilities, Graylog is the right middle ground between Loki’s simplicity and Elasticsearch’s complexity.

4 Comments

  • KlaroNail nail support
    October 17, 2026

    Thanks for explaining when to consult a dermatologist as part of a regular routine.

  • nail fungus treatment options
    October 17, 2026

    Helpful explanation of foods that support general wellness in a balanced way.

  • Memo Honey product information
    October 17, 2026

    Good points about long-term cognitive wellness and everyday wellbeing.

  • Nerve function support
    October 17, 2026

    A clear and helpful article. This is worth bookmarking.

Post Your Comment

Fast • Reliable • Affordable VPS - DO It Now!

Get top VPS hosting with VPS.DO’s fast, low-cost plans. Try risk-free with our 7-day no-questions-asked refund and start today!