Docker graylog-open graylog-datanode - Waiting for datanode certificate renewal

1. Describe your incident:

  • graylog-datanode container:
    • certificate is expired
    • automatic certificate renewal does not work
    • log:
...
2026-06-08T07:18:39.710Z INFO  [cluster] Waiting for server to become available for operation { ping: 1 } with ID 10. Remaining time: 29999 ms. Selector: ReadPreferenceServerSelector{readPreference=primary}, topology description: {type=UNKNOWN, servers=[{address=mongodb:27017, type=UNKNOWN, state=CONNECTING}].
2026-06-08T07:18:39.712Z INFO  [cluster] Monitor thread successfully connected to server with description ServerDescription{address=mongodb:27017, type=STANDALONE, cryptd=false, state=CONNECTED, ok=true, minWireVersion=0, maxWireVersion=21, maxDocumentSize=16777216, logicalSessionTimeoutMinutes=30, roundTripTimeNanos=2958994, minRoundTripTimeNanos=0}
2026-06-08T07:18:39.751Z INFO  [MongoDBPreflightCheck] Connected to MongoDB version 7.0.34
WARNING: A restricted method in java.lang.foreign.Linker has been called
WARNING: java.lang.foreign.Linker::downcallHandle has been called by the unnamed module
WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for this module
2026-06-08T07:18:39.873Z INFO  [MemorySegmentIndexInputProvider] Using MemorySegmentIndexInput and native madvise support with Java 21 or later; to disable start with -Dorg.apache.lucene.store.MMapDirectory.enableMemorySegments=false
2026-06-08T07:18:40.065Z WARN  [VectorizationProvider] Java vector incubator module is not readable. For optimal vector performance, pass '--add-modules jdk.incubator.vector' to enable Vector API.
2026-06-08T07:18:40.552Z INFO  [OpensearchDataDirCompatibilityCheck] Found 47 indices and all of them are valid with current opensearch version 2.19.3
2026-06-08T07:18:40.749Z INFO  [DatanodeKeystoreInitService] Datanode certificate expired on 2026-05-14T08:48:56.000+0000. Requesting and awaiting new certificate
2026-06-08T07:18:40.930Z INFO  [CsrRequesterImpl] Triggered certificate signing request for this datanode
2026-06-08T07:18:40.949Z INFO  [DatanodeKeystoreInitService] Waiting for datanode certificate renewal, retry #1
2026-06-08T07:18:45.979Z INFO  [DatanodeKeystoreInitService] Waiting for datanode certificate renewal, retry #2
2026-06-08T07:18:51.007Z INFO  [DatanodeKeystoreInitService] Waiting for datanode certificate renewal, retry #3
2026-06-08T07:18:56.036Z INFO  [DatanodeKeystoreInitService] Waiting for datanode certificate renewal, retry #4
2026-06-08T07:19:01.065Z INFO  [DatanodeKeystoreInitService] Waiting for datanode certificate renewal, retry #5
2026-06-08T07:19:06.096Z INFO  [DatanodeKeystoreInitService] Waiting for datanode certificate renewal, retry #6
2026-06-08T07:19:11.125Z INFO  [DatanodeKeystoreInitService] Waiting for datanode certificate renewal, retry #7
...
<this goes on forever>
...
  • graylog-mongodb container:
    • does start
    • log:
t={"$date":"2026-06-08T10:17:45.475+00:00"} s=I c=WTCHKPT id=22430 ctx=Checkpointer msg=WiredTiger message attr={"message":{"ts_sec":1780913865,"ts_usec":475702,"thread":"1:0x7f01a8a9c640","session_name":"WT_SESSION.checkpoint","category":"WT_VERB_CHECKPOINT_PROGRESS","category_id":6,"verbose_level":"DEBUG_1","verbose_level_id":1,"msg":"saving checkpoint snapshot min: 80, snapshot max: 80 snapshot count: 0, oldest timestamp: (0, 0) , meta checkpoint timestamp: (0, 0) base write gen: 12096805"}}
  • graylog-graylog (server) container
    • fails to start because it can’t connect to graylog-datanode
    • log:
2026-06-08 10:17:17,464 ERROR: org.graylog2.storage.versionprobe.VersionProbe - Unable to retrieve version from indexer node: Failed to connect to datanode/172.18.0.2:9200. - Connection refused.

2026-06-08 10:17:17,465 INFO : org.graylog2.storage.versionprobe.VersionProbe - Indexer is not available. Retry #582
  • datanode api is not accessible from graylog-graylog, so: curl https://datanode:8999/api/certrenewal/<node_id> is not possible

2. Describe your environment:

docker-compose.yml

services:
  mongodb:
    hostname: "mongodb"
    image: "mongo:7.0"
    restart: "on-failure"
    networks:
      - graylog
    volumes:
      - "mongodb_data:/data/db"
      - "mongodb_config:/data/configdb"

  # ⚠️ Make sure this is set on the host before starting:
  # echo "vm.max_map_count=262144" | sudo tee -a /etc/sysctl.conf
  # sudo sysctl -p
  datanode:
    image: "${DATANODE_IMAGE:-graylog/graylog-datanode:7.1}"
    hostname: "datanode"
    environment:
      GRAYLOG_DATANODE_NODE_ID_FILE: "/var/lib/graylog-datanode/node-id"
      # GRAYLOG_DATANODE_PASSWORD_SECRET and GRAYLOG_PASSWORD_SECRET MUST be the same value
      GRAYLOG_DATANODE_PASSWORD_SECRET: "${GRAYLOG_PASSWORD_SECRET:?Please configure GRAYLOG_PASSWORD_SECRET in the .env file}"
      GRAYLOG_DATANODE_MONGODB_URI: "mongodb://mongodb:27017/graylog"
      GRAYLOG_DATANODE_OPENSEARCH_HEAP: "16g"
      #GRAYLOG_DATANODE_INSECURE_STARTUP: true   # tls cert fix, unsupported ?
    ulimits:
      memlock:
        hard: -1
        soft: -1
      nofile:
        soft: 65536
        hard: 65536
    #ports:
    #  - "8999:8999/tcp"   # DataNode API
    #  - "9200:9200/tcp"
    #  - "9300:9300/tcp"
    networks:
      - graylog
    volumes:
      - "graylog-datanode:/var/lib/graylog-datanode"
    restart: "on-failure"

  graylog:
    hostname: "server"
    image: "${GRAYLOG_IMAGE:-graylog/graylog:7.1}"
    depends_on:
      mongodb:
        condition: "service_started"
      datanode:
        condition: "service_started"
    entrypoint: "/usr/bin/tini --  /docker-entrypoint.sh"
    environment:
      GRAYLOG_NODE_ID_FILE: "/usr/share/graylog/data/data/node-id"
      GRAYLOG_PASSWORD_SECRET: "${GRAYLOG_PASSWORD_SECRET:?Please configure GRAYLOG_PASSWORD_SECRET in the .env file}"
      GRAYLOG_ROOT_PASSWORD_SHA2: "${GRAYLOG_ROOT_PASSWORD_SHA2:?Please configure GRAYLOG_ROOT_PASSWORD_SHA2 in the .env file}"
      GRAYLOG_TIMEZONE: "Europe/Berlin"
      GRAYLOG_HTTP_BIND_ADDRESS: "0.0.0.0:9000"
      #GRAYLOG_HTTP_PUBLISH_URI: "https://graylog.example.com/"
      #GRAYLOG_HTTP_EXTERNAL_URI: "http://127.0.0.1:9000/"
      GRAYLOG_HTTP_EXTERNAL_URI: "https://graylog.example.com/"
      #GRAYLOG_ELASTICSEARCH_HOSTS: "http://opensearch:9200"
      GRAYLOG_ELASTICSEARCH_HOSTS: "https://datanode:9200"
      GRAYLOG_MONGODB_URI: "mongodb://mongodb:27017/graylog"
      GRAYLOG_TRANSPORT_EMAIL_ENABLED: "true"
      GRAYLOG_TRANSPORT_EMAIL_HOSTNAME: "mail.example.com"
      GRAYLOG_TRANSPORT_EMAIL_FROM_EMAIL: "graylog@example.com"
      GRAYLOG_TRANSPORT_EMAIL_PORT: "25"
      GRAYLOG_TRANSPORT_EMAIL_USE_AUTH: "false"
      GRAYLOG_TRANSPORT_EMAIL_USE_TLS: "false"
      GRAYLOG_TRANSPORT_EMAIL_USE_SSL: "false"
      #GRAYLOG_TRUSTED_PROXIES: "127.0.0.1/32, 172.18.0.0/16"
    ports:
    - "514:514/udp"   # Syslog
    - "514:514/tcp"   # Syslog
    #- "5044:5044/tcp"   # Beats
    - "5141:5141/udp"   # Input
    - "5141:5141/tcp"   # Input
    - "5142:5142/udp"   # Input
    - "5142:5142/tcp"   # Input
    - "5143:5143/udp"   # Input
    - "5143:5143/tcp"   # Input
    - "5144:5144/udp"   # Input
    - "5144:5144/tcp"   # Input
    - "5145:5145/udp"   # Input
    - "5146:5146/udp"   # Input
    - "5147:5147/udp"   # Input
    #- "5555:5555/tcp"   # Input
    #- "5555:5555/udp"   # Input
    #- "5001:5001/udp"   # Firewall
    #- "9000:9000/tcp"   # Server API
    #- "12201:12201/tcp" # GELF TCP
    #- "12201:12201/udp" # GELF UDP
    #- "10000:10000/tcp" # Custom TCP port
    #- "10000:10000/udp" # Custom UDP port
    #- "13301:13301/tcp" # Forwarder data
    #- "13302:13302/tcp" # Forwarder config
    networks:
      - graylog
      - caddy
    volumes:
      - "graylog_data:/usr/share/graylog/data/data"
    restart: "on-failure"

networks:
  graylog:
    name: "graylog"
    driver: "bridge"
  caddy:
    external: true

volumes:
  mongodb_data:
  mongodb_config:
  graylog-datanode:
  graylog_data:

3. What steps have you already taken to try and solve the problem?

  • Verified org.graylog2.plugin.certificates.RenewalPolicy is set to automatic: Unable to access GUI after datanode certificate expire
  • Tried every solution I could find in the community
  • Search the docs, community, google, asked ai, github issues → no hint or answer whats wrong or how to fix the issue

4. How can the community help?

Troubleshoot the issue

After trying a lot of stuff it looks like i was able to repair the issue.

Here are some steps I took while messing around.

Rename datanode keystore:

mv /var/lib/docker/volumes/graylog_graylog-datanode/_data/opensearch/config/keystore.jks /var/lib/docker/volumes/graylog_graylog-datanode/_data/opensearch/config/keystore.jks.bak

datanode container environment:

GRAYLOG_DATANODE_INSECURE_STARTUP: true   # tls cert fix, unsupported ?
GRAYLOG_DATANODE_SKIP_PREFLIGHT_CHECKS: "true"
GRAYLOG_DATANODE_ENABLE_TLS_PROTOCOLS: "false"

graylog container environment:

GRAYLOG_ELASTICSEARCH_HOSTS: "http://datanode:9200"
#GRAYLOG_ELASTICSEARCH_HOSTS: "https://datanode:9200"

After some back and forth, i was able to force the datanode container to skip tls and graylog container to connect to the datanode without tls. Than I could renew the datanode certificate via the webinterface.

What a PITA!

I would like to be able to skip the tls part altogether for a small single host installation where lts is no needed if properly secured (i know, it is not recommended,) to avoid all the recurring PAIN that is bound to happen with (broken) certificates.

Pro Tip

Change Certificate Lifetime to 99 years, so hopefully nobody has to waste there life with broken certificates again (not recommended!).

Hi @pixeldoc2000,

The automatic certificate renewal should run automatically, even if your certificate expires and all services have some downtime meanwhile. It runs during the server start, even before the VersionProbe.

I think what happened in your situation is that you have configured GRAYLOG_ELASTICSEARCH_HOSTS, which bypasses datanode autodiscovery, that, before it starts, checks if there are any nodes needing fresh certificates. Otherwise you’d automatically get new cert and both datanode and graylog server would start automatically.

This is quite unexpected situation and I’ll have a look if there is any possible code change that would catch this situation and prevent such problems in the future.

Hi @Tdvorak

the environment variable GRAYLOG_ELASTICSEARCH_HOSTS might be a leftover retained while updating graylog. I’ve started with this setup around graylog 6.0 or 5.

Anyway, thanks for responding.

When removing GRAYLOG_ELASTICSEARCH_HOSTS the containers work, but graylog container complains about even more certificate issues.

2026-06-12 12:42:17,413 INFO : org.mongodb.driver.cluster - Waiting for server to become available for operation profile with ID 8849. Remaining time: 29999 ms. Selector: ReadPreferenceServerSelector{readPreference=primary}, topology description: {type=UNKNOWN, servers=[{address=mongodb:27017, type=UNKNOWN, state=CONNECTING}].
2026-06-12 12:42:17,414 INFO : org.mongodb.driver.cluster - Monitor thread successfully connected to server with description ServerDescription{address=mongodb:27017, type=STANDALONE, cryptd=false, state=CONNECTED, ok=true, minWireVersion=0, maxWireVersion=21, maxDocumentSize=16777216, logicalSessionTimeoutMinutes=30, roundTripTimeNanos=949517, minRoundTripTimeNanos=0}
2026-06-12 12:42:17,502 WARN : org.apache.lucene.internal.vectorization.VectorizationProvider - Java vector incubator module is not readable. For optimal vector performance, pass '--add-modules jdk.incubator.vector' to enable Vector API.
2026-06-12 12:42:18,066 WARN : org.graylog2.notifications.NotificationServiceImpl - There is a notification type we can't handle: [event_limit_reached]
2026-06-12 12:42:34,888 ERROR: org.graylog2.storage.versionprobe.VersionProbe - Unable to retrieve version from indexer node: Hostname 172.18.0.3 not verified:
    certificate: sha256/FFUN83IeSnKrZ95U1UWCKHUEI4Sqi0VB+fCOIE9mVXI=
    DN: CN=datanode
    subjectAltNames: [127.0.0.1, 0:0:0:0:0:0:0:1, 172.18.0.2, localhost, datanode]. - Hostname 172.18.0.3 not verified:
    certificate: sha256/FFUN83IeSnKrZ95U1UWCKHUEI4Sqi0VB+fCOIE9mVXI=
    DN: CN=datanode
    subjectAltNames: [127.0.0.1, 0:0:0:0:0:0:0:1, 172.18.0.2, localhost, datanode].
2026-06-12 12:42:34,889 INFO : org.graylog2.storage.versionprobe.VersionProbe - Indexer is not available. Retry #1/1
2026-06-12 12:42:34,889 ERROR: org.graylog2.storage.versionprobe.VersionProbe - Unable to retrieve version from indexer node: 
com.github.rholder.retry.RetryException: Retrying failed to complete successfully after 1 attempts.
	at com.github.rholder.retry.Retryer.call(Retryer.java:174)
	at org.graylog2.storage.versionprobe.VersionProbeImpl.probe(VersionProbeImpl.java:116)
	at org.graylog2.periodical.SearchVersionCheckPeriodical.doRun(SearchVersionCheckPeriodical.java:113)
	at org.graylog2.plugin.periodical.Periodical.run(Periodical.java:99)
	at com.codahale.metrics.InstrumentedScheduledExecutorService$InstrumentedPeriodicRunnable.run(InstrumentedScheduledExecutorService.java:264)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
	at java.base/java.util.concurrent.FutureTask.runAndReset(Unknown Source)
	at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.base/java.lang.Thread.run(Unknown Source)
2026-06-12 12:43:04,904 ERROR: org.graylog2.storage.versionprobe.VersionProbe - Unable to retrieve version from indexer node: Hostname 172.18.0.3 not verified:
    certificate: sha256/FFUN83IeSnKrZ95U1UWCKHUEI4Sqi0VB+fCOIE9mVXI=
    DN: CN=datanode
    subjectAltNames: [127.0.0.1, 0:0:0:0:0:0:0:1, 172.18.0.2, localhost, datanode]. - Hostname 172.18.0.3 not verified:
    certificate: sha256/FFUN83IeSnKrZ95U1UWCKHUEI4Sqi0VB+fCOIE9mVXI=
    DN: CN=datanode
    subjectAltNames: [127.0.0.1, 0:0:0:0:0:0:0:1, 172.18.0.2, localhost, datanode].
2026-06-12 12:43:04,905 INFO : org.graylog2.storage.versionprobe.VersionProbe - Indexer is not available. Retry #1/1
2026-06-12 12:43:04,906 ERROR: org.graylog2.storage.versionprobe.VersionProbe - Unable to retrieve version from indexer node: 
com.github.rholder.retry.RetryException: Retrying failed to complete successfully after 1 attempts.
	at com.github.rholder.retry.Retryer.call(Retryer.java:174)
	at org.graylog2.storage.versionprobe.VersionProbeImpl.probe(VersionProbeImpl.java:116)
	at org.graylog2.periodical.SearchVersionCheckPeriodical.doRun(SearchVersionCheckPeriodical.java:113)
	at org.graylog2.plugin.periodical.Periodical.run(Periodical.java:99)
	at com.codahale.metrics.InstrumentedScheduledExecutorService$InstrumentedPeriodicRunnable.run(InstrumentedScheduledExecutorService.java:264)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
	at java.base/java.util.concurrent.FutureTask.runAndReset(Unknown Source)
	at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.base/java.lang.Thread.run(Unknown Source)

Currently I am happy graylog is working again, without having to start over.

EDIT: Maybe FQDN is required to get rid of these issues?

Restarting the stack after enable the environment variable GRAYLOG_ELASTICSEARCH_HOSTS again seems to cause new issues.

datanode

2026-06-12T12:56:45.228Z INFO  [OpensearchProcessImpl] [2026-06-12T12:56:45,228][WARN ][o.o.s.a.BackendRegistry  ] [datanode] Authentication finally failed for null from 172.18.0.4:43000

graylog

2026-06-12 12:57:00,258 ERROR: org.graylog2.storage.versionprobe.VersionProbe - Unable to retrieve version from indexer node datanode:9200: unknown error - an exception occurred while deserializing error response: {}

com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'Authentication': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false')

 at [Source: (okio.Buffer$inputStream$1); line: 1, column: 1]

	at com.fasterxml.jackson.core.JsonParser._constructReadException(JsonParser.java:2675)

	at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._reportInvalidToken(UTF8StreamJsonParser.java:3772)

	at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._handleUnexpectedValue(UTF8StreamJsonParser.java:2843)

	at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._nextTokenNotInObject(UTF8StreamJsonParser.java:867)

	at com.fasterxml.jackson.core.json.UTF8StreamJsonParser.nextToken(UTF8StreamJsonParser.java:753)

	at com.fasterxml.jackson.databind.ObjectReader._initForReading(ObjectReader.java:356)

	at com.fasterxml.jackson.databind.ObjectReader._bindAndClose(ObjectReader.java:2135)

	at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:1504)

	at retrofit2.converter.jackson.JacksonResponseBodyConverter.convert(JacksonResponseBodyConverter.java:33)

	at retrofit2.converter.jackson.JacksonResponseBodyConverter.convert(JacksonResponseBodyConverter.java:23)

	at org.graylog2.storage.versionprobe.VersionProbeImpl.lambda$probeSingleHost$2(VersionProbeImpl.java:158)

	at org.graylog2.storage.versionprobe.VersionProbeImpl.rootResponse(VersionProbeImpl.java:213)

	at org.graylog2.storage.versionprobe.VersionProbeImpl.probeSingleHost(VersionProbeImpl.java:168)

	at java.base/java.util.stream.ReferencePipeline$3$1.accept(Unknown Source)

	at java.base/java.util.ArrayList$ArrayListSpliterator.tryAdvance(Unknown Source)

	at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(Unknown Source)

	at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(Unknown Source)

	at java.base/java.util.stream.AbstractPipeline.copyInto(Unknown Source)

	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown Source)

	at java.base/java.util.stream.FindOps$FindOp.evaluateSequential(Unknown Source)

	at java.base/java.util.stream.AbstractPipeline.evaluate(Unknown Source)

	at java.base/java.util.stream.ReferencePipeline.findFirst(Unknown Source)

	at org.graylog2.storage.versionprobe.VersionProbeImpl.probeAllHosts(VersionProbeImpl.java:136)

	at org.graylog2.storage.versionprobe.VersionProbeImpl.lambda$probe$1(VersionProbeImpl.java:116)

	at com.github.rholder.retry.AttemptTimeLimiters$NoAttemptTimeLimit.call(AttemptTimeLimiters.java:78)

	at com.github.rholder.retry.Retryer.call(Retryer.java:160)

	at org.graylog2.storage.versionprobe.VersionProbeImpl.probe(VersionProbeImpl.java:116)

	at org.graylog2.bootstrap.preflight.SearchDbPreflightCheck.runCheck(SearchDbPreflightCheck.java:50)

	at com.google.common.collect.ImmutableList.forEach(ImmutableList.java:421)

	at org.graylog2.bootstrap.preflight.PreflightCheckService.runChecks(PreflightCheckService.java:52)

	at org.graylog2.bootstrap.ServerBootstrap.runPreFlightChecks(ServerBootstrap.java:183)

	at org.graylog2.bootstrap.ServerBootstrap.beforeInjectorCreation(ServerBootstrap.java:151)

	at org.graylog2.bootstrap.CmdLineTool.doRun(CmdLineTool.java:362)

	at org.graylog2.bootstrap.CmdLineTool.run(CmdLineTool.java:287)

	at org.graylog2.bootstrap.Main.main(Main.java:57)

2026-06-12 12:57:00,262 INFO : org.graylog2.storage.versionprobe.VersionProbe - Indexer is not available. Retry #58

Restarting the stack after disable the environment variable GRAYLOG_ELASTICSEARCH_HOSTS seems to fix this issue. graylog does start again.

I don’t want to mess with it any further at this point…

Yeah, I think you are hitting several problems, looking at the logs. The historical GRAYLOG_ELASTICSEARCH_HOSTS is one of them. If you kept the GRAYLOG_DATANODE_INSECURE_STARTUP it will add some more. And finally, the FQDN is yet another issue, docker will change your IP eventually (172.18.0.2 → 172.18.0.3), so a certificate issued to that IP won’t be accepted anymore.

Thank you for your feedback, we’ll try to improve the automatic renewal to prevent some or all of these issues.