jq only tells you whether the JSON is well-formed. To catch unknown parameters, bad field types or invalid settings, you need Elasticsearch itself to parse the JSON. A disposable single-node container does that.
1. Syntax check¶
jq empty test.json && echo "valid JSON"
2. Start a throwaway node¶
docker run --rm -d --name es-lint \
-p 127.0.0.1:19200:9200 \
-e discovery.type=single-node \
-e xpack.security.enabled=false \
-e ES_JAVA_OPTS="-Xms512m -Xmx512m" \
docker.elastic.co/elasticsearch/elasticsearch:9.1.5
until curl -s localhost:19200 >/dev/null; do sleep 2; done
Match the image tag to the version you're targeting. Map only one host port: passing -p 19200:9200 and -p 9200:9200 together also tries to bind host port 9200, which fails if anything else is listening there.
3. Apply the JSON¶
test.json is the request body, for example { "mappings": { ... }, "settings": { ... } }:
curl -s -X PUT localhost:19200/test-mapping \
-H 'Content-Type: application/json' -d @test.json | jq
A good result is "acknowledged": true. A bad one returns an error.root_cause[].reason that names the offending field.
For a whole index template, PUT _index_template/test followed by POST _index_template/_simulate_index/<name> validates it and shows the merged output.
4. Clean up¶
docker stop es-lint # --rm removes the container