A zero-ceremony CLI to spin up a local Splunk instance for log exploration.
splunkctl wraps docker-compose to manage the full lifecycle of a local Splunk container. One command starts Splunk, mounts your log directory, and applies a pre-configured input pipeline — no manual YAML tweaking, no port fumbling.
It handles two common log formats out of the box:
- Plain JSON lines — one JSON object per line
- Prefixed JSON lines — a leading timestamp/stream prefix before the JSON payload
(e.g.2024-01-15T10:00:00Z OUT {"level":"INFO",...})
Spinning up Splunk locally is useful but tedious. You need to know the right Docker flags, wire up volumes, configure inputs.conf and props.conf for your source type, and remember where the UI lives. splunkctl packages all of that into four subcommands so you can go from "I have some logs" to "I can query them in Splunk" in under a minute.
There is no installation step, no package manager, no build. If you have Java and JBang, you run the file and it works.
| Technology | Role |
|---|---|
| JBang | Runs the whole project as a single script; resolves dependencies at runtime |
| Java 17 | Language |
| PicoCLI | CLI parsing, subcommands, --help, properties-file defaults |
docker-compose |
Container lifecycle |
| Apache Commons Exec | Cross-platform process execution |
The entire program is a single Java file (SplunkCtl.java). No build step, no JAR, no native image.
- Java 17+
- JBang
- Docker with the standalone
docker-composebinary
Without cloning — zero install (Linux/macOS):
curl -Ls https://sh.jbang.dev | bash -s - run splunkctl@garodriguezlp/splunkctl startWithout cloning — zero install (Windows PowerShell):
iex "& { $(iwr -useb https://ps.jbang.dev) } run splunkctl@garodriguezlp/splunkctl start"With jbang already installed:
jbang run splunkctl@garodriguezlp/splunkctl startFrom a local clone:
git clone https://github.com/garodriguezlp/splunkctl
cd splunkctl
./jbang splunkctl startSplunk will be available at http://localhost:8000 — log in as admin / changeme123.
The default ~/.splunkctl/samples directory is mounted automatically. Refresh it with fresh timestamps before starting Splunk:
./jbang splunk-samples
./jbang splunkctl start| Command | What it does |
|---|---|
start |
Pull image, wire log directory, start container in detached mode |
stop |
Stop and remove containers (volumes preserved) |
destroy |
Tear down containers and remove all volumes |
status |
Show current container state |
./jbang splunk-samples # refresh ~/.splunkctl/samples with current timestamps
./jbang splunkctl start # defaults: ~/.splunkctl/samples as log dir
./jbang splunkctl start --log-path /path/to/logs
./jbang splunkctl status
./jbang splunkctl stop
./jbang splunkctl destroyDefaults can be overridden with flags, environment variables, or a ~/.splunkctl.properties file:
| Option | Flag | Env var | Default |
|---|---|---|---|
| Log directory | --log-path |
SPLUNK_LOG_PATH |
~/.splunkctl/samples |
| Splunk image | --splunk-image |
SPLUNK_IMAGE |
splunk/splunk:10.0.0 |
| Admin password | --splunk-password |
SPLUNK_PASSWORD |
changeme123 |
Properties file example (~/.splunkctl.properties):
splunk-password=mysecurepassword
log-path=/Users/me/app-logssplunkctl extracts its bundled docker-compose.yml and Splunk defaults into ~/.splunkctl/compose-working-dir and runs docker-compose from there.
-
Runtime-managed sample logs default to
~/.splunkctl/samples. -
Existing legacy files from the old
~/.splunkctllayout are migrated into~/.splunkctl/compose-working-dirautomatically. -
startreuses that working directory and prompts before overwriting an existing complete set of files. -
stop,destroy, andstatusreuse the existing working directory and restore the bundled files automatically if they are missing or incomplete. -
Every command prints the active compose working directory so you can see exactly which files are being used.
Use ./jbang splunk-samples to regenerate the demo files in ~/.splunkctl/samples with timestamps anchored to the current time. That keeps them visible in Splunk's default UI window and makes API examples work with rolling searches like earliest_time=-15m.
The generator writes two files covering both supported formats:
app.log.json: plain JSON lines, each with an in-payloadtsfield.app.log.prefixed: a Docker-style prefix followed by the same JSON payload. The payload keepsts, so after Splunk strips the prefix you still have a timestamp field inside the event.
That duplication is intentional:
- the prefix keeps prefixed logs realistic for the stripping pipeline;
- the JSON
tsfield survives in_rawafter prefix removal; - both files stay aligned around the same event times.
Recommended flow:
./jbang splunk-samples
./jbang splunkctl startIf you want reproducible fixtures instead of "now", pass an explicit base time:
./jbang splunk-samples --base-time 2026-04-07T12:00:00Z* index=main | table _time, level, message
* index=main | stats count by level
* index=main (level=ERROR OR level=WARN) | table _time, level, message
* index=main level=ERROR OR level=WARN | timechart count by level
* index=main exception=* | table _time, class, exception, message
* index=main message="Received request" | stats count by path, status
* index=main durationMs>0 | stats avg(durationMs), max(durationMs) by message | sort -avg(durationMs)
You can verify the stack without opening the Splunk UI by calling the management API on https://localhost:8089.
Use basic auth with the same admin credentials you passed to splunkctl.
Because the local container uses a self-signed certificate, the examples below use -k with curl.
curl -k -u admin:changeme123 \
"https://localhost:8089/services/server/info?output_mode=json"If Splunk is still booting, this request may fail for a short time after splunkctl start returns. Retry until you get a 200 OK response.
This runs a search and streams JSON results back immediately:
curl -k -s -u admin:changeme123 \
https://localhost:8089/services/search/jobs/export \
-d search='search index=main | table _time level message | head 10' \
-d output_mode=jsonExample searches:
curl -k -s -u admin:changeme123 \
https://localhost:8089/services/search/jobs/export \
-d search='search index=main level=ERROR OR level=WARN | table _time level message exception' \
-d output_mode=json
curl -k -s -u admin:changeme123 \
https://localhost:8089/services/search/jobs/export \
-d search='search index=main message="Received request" | stats count by path status' \
-d output_mode=jsonFreshly generated sample files should appear in the default recent time window. If you generated them a while ago, or you used --base-time, adjust the time range explicitly.
You have two practical options:
- In the UI, set the time picker to a wide enough range.
- In the REST API, pass an explicit time range.
For a recent rolling window:
curl -k -s -u admin:changeme123 \
https://localhost:8089/services/search/jobs/export \
-d search='search index=main | table _time level message ts | head 10' \
-d earliest_time='-15m' \
-d latest_time='now' \
-d output_mode=jsonTo search everything regardless of age:
curl -k -s -u admin:changeme123 \
https://localhost:8089/services/search/jobs/export \
-d search='search index=main | table _time level message ts | head 10' \
-d earliest_time=0 \
-d latest_time=now \
-d output_mode=jsonTo search a specific fixed window that includes generated fixtures:
curl -k -s -u admin:changeme123 \
https://localhost:8089/services/search/jobs/export \
-d search='search index=main | table _time level message ts | head 10' \
-d earliest_time='2026-04-07T12:00:00' \
-d latest_time='2026-04-07T12:05:00' \
-d output_mode=jsonThat gives you a clean way to validate both freshly generated sample files and deliberately backdated fixtures.
splunkctl is particularly useful when you want to explore a live log stream from any process — a CLI tool, a deployment job, a migration script — without setting up a dedicated logging pipeline.
The pattern:
- Point
splunkctlat a directory on your machine. - Stream your process output into a file in that directory.
teelets you watch it live in the terminal and write it to disk at the same time. - Open Splunk and query the data however you like.
# Terminal 1 — start Splunk watching a directory
./jbang splunkctl start --log-path ./live-logs
# Terminal 2 — stream output from any process, watch it live and capture it
some-command that produces logs | tee ./live-logs/output.log
# Now explore at http://localhost:8000This works especially well when the process emits structured (JSON) logs — Splunk will parse the fields automatically and make them filterable and chartable without any extra configuration.
When you are done, stop Splunk cleanly:
./jbang splunkctl stop # keeps data volume for next time
./jbang splunkctl destroy # wipes everything for a fresh startMIT