How to Install Graylog on Ubuntu 24.04 VPS: Self-Hosted Log Management and SIEM
Every Linux server, container, firewall, web app, and database emits logs constantly. Without a central place to collect, parse, search, and alert on them, those logs are worse than useless -- they are a liability waiting for an outage or a breach. This guide walks you through installing Graylog Open on an Ubuntu 24.04 VPS alongside MongoDB 6 (metadata) and OpenSearch 2 (search backend), then configures inputs, streams, pipelines, dashboards, alerts, content packs, and an Nginx TLS reverse proxy -- everything you need for a production-grade, self-hosted SIEM.
Prefer to skip the plumbing? Deploy this stack on our CloudCore Professional VPS with 6 vCPU, 12 GB RAM, and 100 GB NVMe for EUR 19.99/month.
Table of Contents
What is Graylog?
Graylog is an open-source centralized log-management and SIEM platform written in Java. It ingests messages from any source that speaks GELF (Graylog Extended Log Format), Syslog, Beats, Kafka, AWS CloudWatch, raw TCP/UDP, or HTTP, then stores them in an OpenSearch (or Elasticsearch) backend with configuration metadata in MongoDB.
Once messages arrive, Graylog gives you:
- Full-text and structured search across billions of events with field-level filters, time-range pickers, and saved queries.
- Streams that route messages matching rules into isolated indices -- think "folders for logs."
- Pipelines with a powerful rule language for parsing, enriching, dropping, and rewriting messages at ingest time.
- Dashboards with timelines, tables, pie charts, heatmaps, world maps (GeoIP), and single-value widgets.
- Alerts fired on aggregation thresholds or correlation rules, delivered to email, Slack, PagerDuty, webhooks, or custom scripts.
- Content packs that bundle inputs, extractors, streams, pipelines, and dashboards for common sources (Linux, Nginx, pfSense, Suricata, Windows Event Log, AWS).
- Role-based access control and LDAP/OAuth/SSO integration.
Why Self-Host Graylog Instead of Using Splunk or Datadog?
Cloud log SaaS products are easy to start with but punish you on volume. Here is how the economics look at a realistic 100 GB/day ingest rate:
| Platform | License model | Cost at 100 GB/day | Data sovereignty | Notes |
|---|---|---|---|---|
| Splunk Cloud | Per-GB ingest | ~USD 5,000+/month | Vendor | 1,500+/GB/month list; heavy discounts required |
| Datadog Logs | Per-GB ingest + per-million-event index | ~USD 3,000+/month | Vendor | Separate fees for retention and rehydration |
| Elastic Cloud | Per-GB storage + per-hour compute | ~USD 1,500+/month | Vendor | Cheaper than Splunk; hot/warm/cold tiering helps |
| Graylog Open (VPS) | Flat VPS + your disk | EUR 19.99-80/month | You | Free software; hardware is your only cost |
No ingestion anxiety. With SaaS pricing, every verbose DEBUG log or chatty microservice triggers a cost conversation. On your own VPS, the only limit is disk -- so you log the things that matter to debugging without worrying about the bill.
Full customizability. You can write arbitrary pipeline rules, extend Graylog with the plugin API, pre-process with Logstash/Vector, and version-control the whole configuration in Git. Try doing that on Datadog.
Reasonable operational cost. A CloudCore Professional VPS at EUR 19.99/month comfortably runs this stack for 20-30 GB/day ingest -- more than enough for a small fleet, an application stack, or a homelab.
Prerequisites
Before you begin, make sure you have:
- A VPS running Ubuntu 24.04 LTS with root or sudo access
- SSH access to the server
- At least 8 GB RAM and 40 GB disk free (100 GB+ recommended for production retention)
- A registered domain name pointing at the VPS public IP (A record), for TLS
- Ports 22, 80, 443 open inbound, plus 12201/udp + 12201/tcp (GELF), 1514/udp (Syslog), and 5044/tcp (Beats) from your log sources
Recommended Plan: CloudCore Professional>
For a single-node Graylog + MongoDB + OpenSearch stack handling up to 30 GB/day ingest, we recommend CloudCore Professional:>
- 6 vCPU cores
- 12 GB RAM
- 100 GB NVMe SSD
- Unmetered bandwidth
- EUR 19.99/month>
For ingest above 50 GB/day, scale OpenSearch onto a dedicated node.
Connect to your server:
ssh root@your-server-ipStep 1: Prepare the Server
Update the system, set a fully qualified hostname, install required utilities, and tune the kernel for OpenSearch.
sudo apt update && sudo apt upgrade -y
sudo apt install -y curl gnupg apt-transport-https ca-certificates \
lsb-release software-properties-common pwgen uuid-runtime ufwSet a hostname:
sudo hostnamectl set-hostname graylog.example.comOpenSearch refuses to start if vm.max_map_count is below 262144. Set it persistently:
sudo tee /etc/sysctl.d/99-opensearch.conf > /dev/null <<EOF
vm.max_map_count=262144
vm.swappiness=1
EOF
sudo sysctl --systemOpen the firewall:
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 12201/udp
sudo ufw allow 12201/tcp
sudo ufw allow 1514/udp
sudo ufw allow 5044/tcp
sudo ufw --force enableStep 2: Install MongoDB 6
Graylog uses MongoDB to store configuration metadata (users, streams, pipeline definitions, saved searches). Graylog 6.x officially supports MongoDB 6.0 and 7.0.
Import the MongoDB 6.0 GPG key and repository:
curl -fsSL https://www.mongodb.org/static/pgp/server-6.0.asc \ | sudo gpg --dearmor -o /usr/share/keyrings/mongodb-server-6.0.gpg
echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-6.0.gpg ] \ https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/6.0 multiverse" \ | sudo tee /etc/apt/sources.list.d/mongodb-org-6.0.list
The jammy codename is used even on Noble because MongoDB 6.0 does not yet ship a Noble-specific package -- the Jammy binaries are ABI-compatible.Install and start:
sudo apt update
sudo apt install -y mongodb-org
sudo systemctl daemon-reload
sudo systemctl enable --now mongod
sudo systemctl status mongod --no-pagerExpected output (abbreviated):
● mongod.service - MongoDB Database Server
Loaded: loaded (/lib/systemd/system/mongod.service; enabled; preset: enabled)
Active: active (running) since ...Verify the connection:
mongosh --eval 'db.runCommand({ connectionStatus: 1 })'MongoDB listens on 127.0.0.1:27017 by default, which is what Graylog needs.
Step 3: Install OpenSearch 2
OpenSearch stores the actual log messages in time-indexed shards and powers the search UI.
Import the OpenSearch 2.x repository:
curl -fsSL https://artifacts.opensearch.org/publickeys/opensearch.pgp \ | sudo gpg --dearmor -o /usr/share/keyrings/opensearch-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/opensearch-keyring.gpg] \ https://artifacts.opensearch.org/releases/bundle/opensearch/2.x/apt stable main" \ | sudo tee /etc/apt/sources.list.d/opensearch-2.x.list
Install OpenSearch. The installer asks for an initial admin password -- for a single-node Graylog setup we will disable the security plugin, but you still have to answer the prompt:
sudo apt update
sudo OPENSEARCH_INITIAL_ADMIN_PASSWORD='TempAdminPass123!' apt install -y opensearchConfigure OpenSearch for a single-node Graylog backend. Replace /etc/opensearch/opensearch.yml content:
sudo tee /etc/opensearch/opensearch.yml > /dev/null <<EOF
cluster.name: graylog
node.name: \${HOSTNAME}
path.data: /var/lib/opensearch
path.logs: /var/log/opensearch
network.host: 127.0.0.1
http.port: 9200
discovery.type: single-node
action.auto_create_index: false
plugins.security.disabled: true
EOFDisabling the security plugin is appropriate here because OpenSearch binds to 127.0.0.1 and only Graylog on the same host talks to it. If you move OpenSearch to a dedicated node, re-enable the security plugin and configure TLS + credentials in Graylog.Tune the JVM heap to roughly half the VPS RAM, capped at 31 GB. For a 12 GB server, 4 GB is a good choice:
sudo sed -i 's/^-Xms.*/-Xms4g/' /etc/opensearch/jvm.options
sudo sed -i 's/^-Xmx.*/-Xmx4g/' /etc/opensearch/jvm.optionsStart and enable:
sudo systemctl daemon-reload
sudo systemctl enable --now opensearchWait ~30 seconds, then verify:
curl -s http://127.0.0.1:9200 | head -20Expected output:
{
"name" : "graylog.example.com",
"cluster_name" : "graylog",
"cluster_uuid" : "...",
"version" : {
"distribution" : "opensearch",
"number" : "2.15.0",
...
},
"tagline" : "The OpenSearch Project: https://opensearch.org/"
}Step 4: Install Graylog Open
Add the official Graylog 6.x repository. Graylog ships the repository as a .deb package that drops in the apt source and the signing key.
wget https://packages.graylog2.org/repo/packages/graylog-6.1-repository_latest.deb
sudo dpkg -i graylog-6.1-repository_latest.deb
sudo apt update
sudo apt install -y graylog-serverThe package creates the graylog system user, installs the server JAR in /usr/share/graylog-server, and lays down the config skeleton at /etc/graylog/server/server.conf.
Step 5: Configure server.conf
Graylog needs four critical settings before it will start: password_secret, root_password_sha2, http_bind_address, and http_publish_uri.
Generate a long, random password secret:
PASSWORD_SECRET=$(pwgen -N 1 -s 96)
echo "password_secret: $PASSWORD_SECRET"Pick an admin password and hash it. Graylog expects the SHA-256 hash of the plain password:
read -s -p "Enter admin password: " ADMIN_PASSWORD && echo
ROOT_PASSWORD_SHA2=$(echo -n "$ADMIN_PASSWORD" | sha256sum | awk '{print $1}')
echo "root_password_sha2: $ROOT_PASSWORD_SHA2"Edit /etc/graylog/server/server.conf. The four lines to locate and set are:
sudo sed -i "s|^password_secret =.*|password_secret = $PASSWORD_SECRET|" /etc/graylog/server/server.conf
sudo sed -i "s|^root_password_sha2 =.*|root_password_sha2 = $ROOT_PASSWORD_SHA2|" /etc/graylog/server/server.conf
sudo sed -i "s|^#\?http_bind_address =.*|http_bind_address = 0.0.0.0:9000|" /etc/graylog/server/server.conf
sudo sed -i "s|^#\?http_publish_uri =.*|http_publish_uri = https://graylog.example.com/|" /etc/graylog/server/server.confOther useful values to confirm in /etc/graylog/server/server.conf:
root_timezone = UTC
root_email = [email protected]
elasticsearch_hosts = http://127.0.0.1:9200
mongodb_uri = mongodb://localhost/graylog
message_journal_dir = /var/lib/graylog-server/journal
message_journal_max_size = 5gb
processbuffer_processors = 2
outputbuffer_processors = 2Tune the Graylog JVM heap. For a 12 GB VPS, 2 GB is a safe default:
sudo sed -i 's|^#\?GRAYLOG_SERVER_JAVA_OPTS=.*|GRAYLOG_SERVER_JAVA_OPTS="-Xms2g -Xmx2g -XX:+UseG1GC -XX:-OmitStackTraceInFastThrow"|' \
/etc/default/graylog-serverStep 6: Start Graylog and Log In
sudo systemctl daemon-reload
sudo systemctl enable --now graylog-server
sudo journalctl -u graylog-server -fThe first boot takes 30-90 seconds while Graylog provisions its MongoDB collections and initial OpenSearch index set. You are looking for a line like:
INFO [ServerBootstrap] Graylog server up and running.Press Ctrl+C to exit the log follow.
Open http://your-server-ip:9000 in a browser. Log in as admin with the password you hashed in Step 5.
You should land on the Graylog dashboard. Before shipping any production traffic, lock the UI behind Nginx + TLS in Step 10.
Step 7: Configure Inputs (GELF, Syslog, Beats)
Inputs are listeners that accept log traffic. Navigate to System -> Inputs in the UI.
GELF UDP
Best for application logs from Docker (--log-driver=gelf), docker-compose, Kubernetes (fluent-bit), and libraries like python-graypy or winston-graylog2.
GELF UDP
- Bind address: 0.0.0.0
- Port: 12201
Test from the server:
echo -e '{ "version": "1.1", "host": "test-host", "short_message": "Hello Graylog", "level": 6 }' \
| nc -u -w1 127.0.0.1 12201A Hello Graylog message should appear under Search within a second.
GELF TCP
Same as above but over TCP, preferred for WAN links where UDP packet loss is a concern. Use the same port (12201/tcp) or pick another.
Syslog UDP
For network devices (firewalls, switches, load balancers) and Linux rsyslog/syslog-ng.
1514 (non-privileged, avoids needing CAP_NET_BIND_SERVICE).full_message.Point rsyslog at it by adding to /etc/rsyslog.d/50-graylog.conf on a client:
. @graylog.example.com:1514;RSYSLOG_SyslogProtocol23FormatBeats
For Filebeat, Winlogbeat, Metricbeat, and other Elastic Beats shippers.
0.0.0.0, Port: 5044.Install Filebeat on a remote host and configure /etc/filebeat/filebeat.yml:
filebeat.inputs:
- type: filestream
id: system-logs
paths:
- /var/log/*.log
- /var/log/syslog
output.logstash:
hosts: ["graylog.example.com:5044"]Restart Filebeat; messages start flowing in seconds.
Step 8: Streams, Pipelines, Dashboards, and Alerts
Streams
Streams are real-time classifiers. Every incoming message runs through stream rules; matching messages are written to the stream's indices and become available for dedicated searches, dashboards, and alerts.
Example: a Production Errors stream.
Production Errors, Description: Error-level logs from production services.level is less than or equal to 3 (syslog ERROR+).environment is exactly production.Every matching message is now double-written to the stream. You can build alerts and dashboards scoped to only this stream.
Pipelines
Pipelines transform messages as they arrive. They use a domain-specific rule language and run inside stages -- higher-stage rules see the changes from lower-stage rules.
Create a rule that extracts HTTP status codes from Nginx access logs:
Extract Nginx HTTP status.rule "Extract Nginx HTTP status"
when
has_field("message") AND contains(to_string($message.source), "nginx")
then
let status = regex("HTTP/\\d\\.\\d\" (\\d{3})", to_string($message.message));
set_field("http_status", to_long(status["0"]));
endPipelines also handle enrichment (GeoIP lookups on src_ip), redaction (masking PII before storage), and drop rules to kill noisy messages at ingest.
Dashboards
Build a dashboard:
Production Overview.timestamp, Columns: level, Metric: count().count() for last-5-minute errors, a pie chart on source, a world map from GeoIP src_ip_geoip, and a table of top 10 http_status.Alerts (Event Definitions)
High error rate, Priority: High.level:<=3 AND environment:production.Production Errors.5 minutes. Execute every 1 minute.count() is greater than 50.source.When 50+ errors appear in 5 minutes from a single source, Graylog fires the notification and creates an Event record in the Events browser.
Step 9: Install Content Packs
Content packs bundle inputs, extractors, pipelines, dashboards, and stream rules for common log sources. Graylog maintains the Graylog Marketplace and offers a free tier of Illuminate (the official content pack collection).
Install a community content pack
Nginx Access Logs).Enable Illuminate (optional)
Illuminate is Graylog's curated content library with parsed fields, normalized schemas, and SIEM-ready detections for Windows Event Log, Linux, Palo Alto, Cisco, Okta, O365, and dozens more. The free tier is accessible from any Graylog deployment:
Step 10: Publish Behind Nginx with TLS
Never expose the Graylog web UI (port 9000) directly on the public internet -- even with strong credentials, browser cookies should only cross TLS. Install Nginx and Certbot:
sudo apt install -y nginx certbot python3-certbot-nginxCreate the site config:
sudo tee /etc/nginx/sites-available/graylog > /dev/null <<'EOF' server { listen 80; server_name graylog.example.com; return 301 https://$host$request_uri; }server { listen 443 ssl http2; server_name graylog.example.com;
ssl_certificate /etc/letsencrypt/live/graylog.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/graylog.example.com/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "DENY" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always;
client_max_body_size 50m;
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://$host/;
proxy_read_timeout 180s; proxy_send_timeout 180s; proxy_buffering off; } } EOF
sudo ln -s /etc/nginx/sites-available/graylog /etc/nginx/sites-enabled/ sudo nginx -t
Issue the certificate:
sudo certbot --nginx -d graylog.example.com --redirect --agree-tos -m [email protected] -n
sudo systemctl reload nginxUpdate /etc/graylog/server/server.conf so Graylog knows it is behind a proxy:
http_publish_uri = https://graylog.example.com/
http_external_uri = https://graylog.example.com/
trusted_proxies = 127.0.0.1/32Restart Graylog:
sudo systemctl restart graylog-serverVisit https://graylog.example.com/ and log in.
Post-Install Hardening
- Rotate the admin password by updating
root_password_sha2inserver.confand restarting, or disableroot_password_sha2entirely and create a dedicated admin user through the UI. - Enable LDAP/OAuth via System -> Authentication for team access instead of the single
rootuser. - Retention policies: System -> Indices lets you set rotation (daily or by size) and retention (delete, close, or archive after N indices).
- Back up MongoDB daily:
mongodump --out /backup/mongodb/$(date +%F). - Monitor the journal: Graylog buffers messages in
/var/lib/graylog-server/journalwhen OpenSearch is slow. Alert if journal size approachesmessage_journal_max_size. - Ship Graylog's own logs to itself via a GELF appender in
/etc/graylog/server/log4j2.xmlso that Graylog failures are visible in Graylog once it recovers.
Troubleshooting
| Problem | Cause | Solution |
|---|---|---|
graylog-server fails to start with password_secret error | Missing or too-short secret in server.conf | Regenerate: pwgen -N 1 -s 96 and set in /etc/graylog/server/server.conf |
Could not connect to Elasticsearch in Graylog logs | OpenSearch not running or security plugin still enabled | sudo systemctl status opensearch; confirm plugins.security.disabled: true in opensearch.yml; curl http://127.0.0.1:9200 |
OpenSearch fails: max virtual memory areas vm.max_map_count is too low | vm.max_map_count not applied | sudo sysctl -w vm.max_map_count=262144; persist via /etc/sysctl.d/99-opensearch.conf |
| UI loads but "Processing disabled" banner | Journal is full or indexer blocked | Check /var/lib/graylog-server/journal size; inspect System -> Overview for indexer errors; increase OpenSearch heap |
| 502 Bad Gateway from Nginx | Graylog not listening or wrong http_bind_address | Confirm http_bind_address = 0.0.0.0:9000; curl http://127.0.0.1:9000 from the VPS |
pwgen: command not found | Utility not installed | sudo apt install -y pwgen |
Login loop / 400 on POST to /api/ | http_publish_uri or http_external_uri misconfigured | Set both to the public HTTPS URL ending with /; restart Graylog |
| MongoDB fails on Ubuntu 24.04 with libssl errors | Noble ships OpenSSL 3; MongoDB 6 uses jammy binaries | The jammy-compatibility library is automatically pulled; if not, sudo apt install -y libssl3 |
sudo journalctl -u graylog-server -n 200 --no-pager
sudo journalctl -u opensearch -n 200 --no-pager
sudo tail -f /var/log/graylog-server/server.logFAQ
What is the difference between Graylog Open and Graylog Enterprise?
Graylog Open is the free, source-available edition released under the SSPL. It includes ingest, search, streams, pipelines, dashboards, alerts, and content packs -- everything this guide installs. Graylog Enterprise (commercial) adds audit logs, archiving to cold storage, scheduled reports, parallelized searches across archived indices, teams, and official vendor support. For most SMB and mid-market deployments handling up to a few hundred GB/day, Graylog Open covers 100 percent of the core SIEM and log-management workflow. If you need compliance-grade audit trails or cold-storage archiving across years of retention, evaluate Enterprise.
Can I use Elasticsearch instead of OpenSearch?
Graylog 6.x officially supports OpenSearch 2.x and Elasticsearch 7.10 only. New installations should choose OpenSearch because Elasticsearch 7.10 is the last Apache-2.0/Elastic License compatible release and has been in end-of-life since 2022. OpenSearch is Apache-2.0 licensed and actively developed by AWS and the community. Moving to Elasticsearch 8.x is not supported because Graylog uses legacy client APIs that were removed in Elasticsearch 8. If you already run Elasticsearch 7.10, Graylog will connect -- but plan a migration to OpenSearch.
How much RAM does Graylog need?
A single-node Graylog + MongoDB + OpenSearch on the same VPS needs at least 8 GB RAM. Allocate 2-4 GB to the JVM heap for OpenSearch (-Xms -Xmx in /etc/opensearch/jvm.options), 1-2 GB to the Graylog JVM (GRAYLOG_SERVER_JAVA_OPTS), and leave 2 GB for MongoDB and the OS page cache. For production ingest above 50 GB/day, move OpenSearch to a dedicated node with 16 GB RAM and a separate data disk. Disk-wise, budget roughly 1.5x your daily log volume times your retention in days (raw size times OpenSearch's compression ratio times replica count).
Is Graylog a full SIEM?
Graylog Open provides the log-ingest, parsing, correlation, alerting, and dashboarding foundation of a SIEM. Pair it with complementary open-source tools to round out the capabilities: Wazuh for host-based intrusion detection and file integrity monitoring, Suricata or Zeek for network intrusion detection, and the free tier of Graylog Illuminate for pre-built detection content. Together, these stacks give you a full SIEM without Splunk's per-GB licensing fees or Datadog's per-event surcharges.
How do I ship logs from a remote server to Graylog?
Three common options:
/etc/rsyslog.d/50-graylog.conf and all syslog messages forward.--log-driver=gelf flag in Docker, or python-graypy directly in Python. GELF preserves field types natively, so integers stay integers and you do not need extractors.For TLS, terminate on Graylog's Beats input (supports TLS natively) or front a raw TCP input with stunnel.
Next Steps
Now that Graylog is running, build out the rest of your observability stack:
- Install Loki on Ubuntu -- Pair Graylog (SIEM/structured queries) with Loki (label-indexed, Grafana-native) to compare which fits each use case, or run them side by side for defense in depth.
- Install Wazuh on Ubuntu -- Add host-based intrusion detection, FIM, and vulnerability scanning. Wazuh's agent forwards alerts to Graylog via Syslog or Kafka for unified correlation.
- Install OpenSearch on Ubuntu -- Scale out the OpenSearch tier to a dedicated node with data/master separation, replica shards, and hot/warm index lifecycle policies for production-grade retention.
- Read the official docs at go2docs.graylog.org for the complete reference on pipelines, GROK patterns, lookup tables, and the REST API.
- Version-control your configuration -- export content packs to JSON and commit them to Git so that streams, pipelines, and dashboards are reproducible across dev/staging/prod.
Skip the Manual Install -- Deploy Graylog on CloudCore Professional>
Every step in this guide runs comfortably on our CloudCore Professional VPS:>
- 6 vCPU cores, 12 GB RAM, 100 GB NVMe SSD
- Unmetered bandwidth on a 1 Gbit/s port
- Ubuntu 24.04 LTS pre-installed
- Full root access, hourly snapshots optional
- EUR 19.99/month, deployable in under 60 seconds>
Launch a CloudCore Professional VPS and follow this guide end to end.