Search is coming soon — browse Launches, Reviews, or Best Tools from the nav for now.

Tutorial · beginner·By Marcus Webb·15 minutes·OpenObserve

Self-Host OpenObserve in Docker and Ingest Your First Logs

Prerequisites

  • · Docker installed and running
  • · curl and unzip available on your machine
OpenObserve logo, the open-source observability platformDeveloper

What you’ll build

A local, single-container OpenObserve instance with real log data loaded into it — enough to see how the platform ingests, stores, and queries telemetry before you commit to running it against a production workload. No Kubernetes cluster or multi-node setup required; OpenObserve deploys as one binary, and the Docker image is the fastest way to try that for yourself.

By the end, you’ll have ingested 1,000 sample log lines, run both UI filters and raw SQL against them, and know where to point real applications once you’re ready to move past the sample dataset.

Step 1: Run OpenObserve via Docker

Pull and start the OSS image, mapping a local data folder for persistence and setting the root user’s credentials through environment variables:

docker run -d --name openobserve \
  -v $PWD/data:/data \
  -e ZO_DATA_DIR="/data" \
  -e ZO_ROOT_USER_EMAIL="root@example.com" \
  -e ZO_ROOT_USER_PASSWORD="Complexpass#123" \
  -p 5080:5080 \
  openobserve/openobserve:latest

This exposes the UI and ingestion API on port 5080 and gives you a durable data directory on the host, so the container can be stopped and restarted without losing what you’ve ingested. Use a real password of your own rather than the example above if this instance will be reachable by anyone other than you — even for local testing, ZO_ROOT_USER_PASSWORD is the only thing standing between an open port and your data.

Confirm the container actually started before moving on:

docker ps --filter name=openobserve
docker logs openobserve --tail 20

docker ps should show the container as Up; the logs should settle into a steady state without repeated panics or restarts. If the container exits immediately, jump to Troubleshooting below before continuing.

Step 2: Log in to the UI

Open http://localhost:5080 in a browser and sign in with the root credentials you set above (root@example.com / Complexpass#123). You should land on an empty dashboard — that’s expected, since no data has been ingested yet.

Take a moment to look at the left sidebar: Logs, Metrics, Traces, Dashboards, and Streams all live under the same UI, which is the core pitch of a unified observability platform versus running separate tools for each telemetry type.

Step 3: Download sample log data

OpenObserve’s own docs ship a sample Kubernetes log set that’s useful for a first test run:

curl -L https://zinc-public-data.s3.us-west-2.amazonaws.com/zinc-enl/sample-k8s-logs/k8slog_json.json.zip -o k8slog_json.json.zip
unzip k8slog_json.json.zip

You should end up with a k8slog_json.json file in the current directory — each line is a JSON object representing one log event from a Kubernetes cluster, with fields like kubernetes_pod_name, log, and level already present.

Step 4: Ingest the logs

Post the JSON file to the default organization and stream via OpenObserve’s HTTP ingestion API:

curl -u "root@example.com:Complexpass#123" \
  -H "Content-Type: application/json" \
  http://localhost:5080/api/default/default/_json \
  -d "@k8slog_json.json"

A successful response looks like {"code":200,"status":"ok","records":1000} — confirmation that 1,000 log lines just landed in the default stream. If you instead get a 401, double-check the -u credentials match the environment variables from Step 1 exactly; if you get a connection-refused error, the container isn’t listening on 5080 yet — re-check docker ps.

Step 5: Query the data

Back in the UI, click Logs in the sidebar and select the default stream. Try a filter like level='error' in the search bar to narrow the 1,000 ingested records down to just the failures — this is the same SQL-like query syntax you’d use against metrics or traces once you’re ingesting those too.

A few other things worth trying before you move on:

  • Time range — the picker in the top-right defaults to the last 15 minutes. Since the sample data has its own historical timestamps, widen it (try “Last 7 days” or a custom range) if your first filter comes back empty.
  • SQL mode — toggle SQL Mode above the search bar and run a real query, e.g. SELECT kubernetes_pod_name, count(*) FROM "default" WHERE level='error' GROUP BY kubernetes_pod_name ORDER BY count(*) DESC. This is the same query engine used to power dashboards, so getting comfortable with it here pays off later.
  • Histogram view — the bar chart above the results buckets log volume over time, which is useful for spotting spikes even before you read a single log line.

Troubleshooting

  • Container exits right after starting — run docker logs openobserve (without --tail) to see the full startup error. The most common cause is a stale data volume from a previous run with different root credentials; if you don’t need that data, stop the container, delete the local data folder, and re-run the docker run command from Step 1.
  • Port 5080 already in use — another process (often a previous OpenObserve container that didn’t get removed) is bound to it. Run docker ps -a to find it, remove it with docker rm -f <container-id>, or change the host-side port mapping to something like -p 5081:5080 and adjust the URLs in this guide accordingly.
  • Login page loads but credentials are rejected — the root user is only created on a completely fresh data directory. If you changed ZO_ROOT_USER_EMAIL or ZO_ROOT_USER_PASSWORD after the first run, they won’t take effect against an existing data folder; clear it and restart the container to re-apply them.
  • Ingestion returns 200 but the Logs view shows nothing — check the time range picker first (see Step 5); it’s the single most common reason data looks “missing” when it’s actually just outside the visible window.

Where to go from here

This single-container setup is meant for evaluation, not production — OpenObserve’s docs cover Helm charts and clustered deployments for that. But the ingestion path doesn’t change: once you’re pointing real applications at this instance via its OpenTelemetry-compatible endpoints instead of the sample-data curl command, the same Logs view (plus Metrics, Traces, and the LLM observability panel) becomes your day-to-day monitoring surface — an OpenTelemetry Collector configured with an OTLP/HTTP exporter pointed at http://localhost:5080/api/default is the standard way to start feeding it real traffic instead of the sample dataset.

When you’re done testing, tear the container down with docker stop openobserve && docker rm openobserve — the data folder on your host will still be there if you want to spin it back up later with the same credentials and history intact.