banross,
Setting up Graylog using Docker Compose is super easy (once you figure it out
). I have been running my setup for a while and I followed the advice from Tom’s video when setting it up the first time.
I have two examples for you that you can almost literary copy/paste into a Linux server (with Docker). I run mine in a Ubuntu 24.04.1 LTS server (as a Proxmox VM).
The difference between the two examples is the network setup.
The first example uses the Docker bridge network driver (which is what I have seen most people do when running containers at home). You will have to map all the ports you want and modify and re-deploy the stack when you want to map more ports.
The second one uses a Docker macvlan network driver which allows you to specify a dedicated IP for your container. In this case you do not have to do any port mapping because you are addressing the container by its assigned IP address. I find this cleaner and gives you the opportunity to give your container a proper DNS record internal to your network. Creating the macvlan network is something you would have to do independently and externally to this configuration for it work though. Nonetheless, it is pretty straightforward and you can easily find instructions online.
I tried to put comments on both examples to tell you what needs to be changed to fit your environment. The most important things to change/notice are:
- Network setup
- Volume mapping
- Passwords for OpenSearch and Graylog
Here is the example for my “graylog stack” using the bridge network driver:
services:
mongodb:
image: "mongo:6.0.20"
ports:
- "27017:27017"
restart: "unless-stopped"
networks:
- graylog
volumes:
- "/opt/docker/graylog-stack/mongodb/data:/data/db" # <-- CHANGE ME WITH YOUR PATH. In this example, it is /opt/docker/graylog-stack/mongodb/data
container_name: graylog-stack-mongodb
opensearch:
image: "opensearchproject/opensearch:2.18.0"
environment:
- "OPENSEARCH_JAVA_OPTS=-Xms1g -Xmx1g"
- "bootstrap.memory_lock=true"
- "discovery.type=single-node"
- "action.auto_create_index=false"
- "plugins.security.ssl.http.enabled=false"
- "plugins.security.disabled=true"
# Can generate a password for `OPENSEARCH_INITIAL_ADMIN_PASSWORD` using a linux device via:
# tr -dc A-Z-a-z-0-9_@#%^-_=+ < /dev/urandom | head -c${1:-32}
- "OPENSEARCH_INITIAL_ADMIN_PASSWORD=<REDACTED>" # <-- CHANGE ME WITH GENERATED PASSWORD
ulimits:
memlock:
hard: -1
soft: -1
nofile:
soft: 65536
hard: 65536
ports:
- "9200:9200"
- "9300:9300"
restart: "unless-stopped"
networks:
- graylog
volumes:
- "/opt/docker/graylog-stack/opensearch/data:/usr/share/opensearch/data" # <-- CHANGE ME WITH YOUR PATH. In this example, it is /opt/docker/graylog-stack/opensearch/data
container_name: graylog-stack-opensearch
graylog:
container_name: graylog-stack-graylog
image: "graylog/graylog:6.1.5"
depends_on:
mongodb:
condition: "service_started"
opensearch:
condition: "service_started"
entrypoint: "/usr/bin/tini -- wait-for-it opensearch:9200 -- /docker-entrypoint.sh"
environment:
GRAYLOG_NODE_ID_FILE: "/usr/share/graylog/data/config/node-id"
GRAYLOG_HTTP_BIND_ADDRESS: "0.0.0.0:9000"
GRAYLOG_ELASTICSEARCH_HOSTS: "http://opensearch:9200"
GRAYLOG_MONGODB_URI: "mongodb://mongodb:27017/graylog"
GRAYLOG_REPORT_DISABLE_SANDBOX: "true"
GRAYLOG_PASSWORD_SECRET: "<REDACTED>" # <-- CHANGE ME WITH YOUR PASSWORD SECRET (must be at least 16 characters)
GRAYLOG_ROOT_PASSWORD_SHA2: "<REDACTED>" # <-- CHANGE ME WITH YOUR PASSWORD SHA2
GRAYLOG_HTTP_EXTERNAL_URI: "http://127.0.0.1:9000/"
GRAYLOG_TIMEZONE: "America/New_York" # <-- CHANGE ME WITH YOUR TIMEZONE
TZ: "America/New_York" # <-- CHANGE ME WITH YOUR TIMEZONE
ports:
# Graylog web interface and REST API
- "9000:9000/tcp"
# Beats
- "5044:5044/tcp"
# Syslog TCP
- "5140:5140/tcp"
# Syslog UDP
- "5140:5140/udp"
# GELF TCP
- "12201:12201/tcp"
# GELF UDP
- "12201:12201/udp"
# Forwarder data
- "13301:13301/tcp"
# Forwarder config
- "13302:13302/tcp"
#
# BELOW ARE EXAMPLES OF ADDITIONAL PORTS FOR OTHER LOG SOURCES THAT I USE. YOU DON'T NECESSARILY NEED THEM ALL.
# unifi
- "33123:33123/tcp"
# unifi
- "33123:33123/udp"
# pfsense
- "33124:33124/tcp"
# pfsense
- "33124:33124/udp"
# truenas
- "33125:33125/tcp"
# truenas
- "33125:33125/udp"
# proxmox
- "33126:33126/tcp"
# proxmox
- "33126:33126/udp"
# synology
- "33127:33127/tcp"
# synology
- "33127:33127/udp"
restart: "unless-stopped"
networks:
- graylog
volumes:
- "/opt/docker/graylog-stack/graylog/data:/usr/share/graylog/data/data" # <-- CHANGE ME WITH YOUR PATH. In this example, it is /opt/docker/graylog-stack/graylog/data
- "/opt/docker/graylog-stack/graylog/config:/usr/share/graylog/data/config" # <-- CHANGE ME WITH YOUR PATH. In this example, it is /opt/docker/graylog-stack/graylog/config
- "/opt/docker/graylog-stack/graylog/journal:/usr/share/graylog/data/journal" # <-- CHANGE ME WITH YOUR PATH. In this example, it is /opt/docker/graylog-stack/graylog/journal
networks:
graylog:
driver: "bridge"
Here is the example for my “graylog stack” using the macvlan network driver:
services:
mongodb:
image: "mongo:6.0.20"
restart: "unless-stopped"
networks:
dmz:
ipv4_address: 10.10.10.10 # <-- CHANGE ME WITH YOUR IP ADDRESS
volumes:
- "/opt/docker/graylog-stack/mongodb/data:/data/db" # <-- CHANGE ME WITH YOUR PATH. In this example, it is /opt/docker/graylog-stack/mongodb/data
container_name: graylog-stack-mongodb
opensearch:
image: "opensearchproject/opensearch:2.18.0"
environment:
- "OPENSEARCH_JAVA_OPTS=-Xms1g -Xmx1g"
- "bootstrap.memory_lock=true"
- "discovery.type=single-node"
- "action.auto_create_index=false"
- "plugins.security.ssl.http.enabled=false"
- "plugins.security.disabled=true"
# Can generate a password for `OPENSEARCH_INITIAL_ADMIN_PASSWORD` using a linux device via:
# tr -dc A-Z-a-z-0-9_@#%^-_=+ < /dev/urandom | head -c${1:-32}
- "OPENSEARCH_INITIAL_ADMIN_PASSWORD=<REDACTED>" # <-- CHANGE ME WITH GENERATED PASSWORD
ulimits:
memlock:
hard: -1
soft: -1
nofile:
soft: 65536
hard: 65536
restart: "unless-stopped"
networks:
dmz:
ipv4_address: 10.10.10.11 # <-- CHANGE ME WITH YOUR IP ADDRESS
volumes:
- "/opt/docker/graylog-stack/opensearch/data:/usr/share/opensearch/data" # <-- CHANGE ME WITH YOUR PATH. In this example, it is /opt/docker/graylog-stack/opensearch/data
container_name: graylog-stack-opensearch
graylog:
container_name: graylog-stack-graylog
image: "graylog/graylog:6.1.5"
depends_on:
mongodb:
condition: "service_started"
opensearch:
condition: "service_started"
entrypoint: "/usr/bin/tini -- wait-for-it opensearch:9200 -- /docker-entrypoint.sh"
environment:
GRAYLOG_NODE_ID_FILE: "/usr/share/graylog/data/config/node-id"
GRAYLOG_HTTP_BIND_ADDRESS: "0.0.0.0:9000"
GRAYLOG_ELASTICSEARCH_HOSTS: "http://opensearch:9200"
GRAYLOG_MONGODB_URI: "mongodb://mongodb:27017/graylog"
GRAYLOG_REPORT_DISABLE_SANDBOX: "true"
GRAYLOG_PASSWORD_SECRET: "<REDACTED>" # <-- CHANGE ME WITH YOUR PASSWORD SECRET (must be at least 16 characters)
GRAYLOG_ROOT_PASSWORD_SHA2: "<REDACTED>" # <-- CHANGE ME WITH YOUR PASSWORD SHA2
GRAYLOG_HTTP_EXTERNAL_URI: "http://127.0.0.1:9000/"
GRAYLOG_TIMEZONE: "America/New_York" # <-- CHANGE ME WITH YOUR TIMEZONE
TZ: "America/New_York" # <-- CHANGE ME WITH YOUR TIMEZONE
restart: unless-stopped
networks:
dmz: # <-- THIS IS A MACVLAN NETWORK CREATED OUTSIDE THIS FILE. THE NAME CAN BE DIFFERENT. IN THIS EXAMPLE, IT IS `dmz`.
ipv4_address: 10.10.10.12 # <-- CHANGE ME WITH YOUR IP ADDRESS
volumes:
- "/opt/docker/graylog-stack/graylog/data:/usr/share/graylog/data/data" # <-- CHANGE ME WITH YOUR PATH. In this example, it is /opt/docker/graylog-stack/graylog/data
- "/opt/docker/graylog-stack/graylog/config:/usr/share/graylog/data/config" # <-- CHANGE ME WITH YOUR PATH. In this example, it is /opt/docker/graylog-stack/graylog/config
- "/opt/docker/graylog-stack/graylog/journal:/usr/share/graylog/data/journal" # <-- CHANGE ME WITH YOUR PATH. In this example, it is /opt/docker/graylog-stack/graylog/journal
networks:
dmz: # <-- THIS IS A MACVLAN NETWORK CREATED OUTSIDE THIS FILE. THE NAME CAN BE DIFFERENT. IN THIS EXAMPLE, IT IS `dmz`.
name: dmz # <-- THIS IS A MACVLAN NETWORK CREATED OUTSIDE THIS FILE. THE NAME CAN BE DIFFERENT. IN THIS EXAMPLE, IT IS `dmz`.
external: true
Both of these YAML files are using the latest versions of each container in the stack (I just re-deployed mine as I was writing this to make sure they still worked).
As far as deploying the stack, you can use Docker Compose from the CLI but I have grown fond of using Portainer (https://www.portainer.io). You can deploy Portainer as a container (https://hub.docker.com/r/portainer/portainer-ce) and use it to manage other containers and build stacks. It makes maintenance and upgrades super easy… and sometimes it is nice to have a pretty GUI.
Anyways, hope this helps. I know I spent a ton of time trying to figure out logging for my home environment before landing on this setup (and I am really happy with it).
Regards,
DrMofongo