Skip to content

How to Deploy OpenObserve on a VPS

Updated Sep 2026

verified on Ubuntu 26.04 · Sep 2026
We earn commissions when you shop through the links below. Full disclosure →

Self-host OpenObserve, a single-binary store for logs, metrics and traces, on a VPS with Docker Compose. Generate root credentials, keep it on loopback behind Caddy, and set retention before the default ten years.

Before you start
  • A VPS with 1 GB RAM or more (2 GB+ once real log volume arrives)
  • A fresh Ubuntu 24.04 or 26.04 server with root/sudo SSH access
  • A domain you can point at the server
  • Docker Engine + Compose installed (see the base guide below)
Need a box for this guide? Kamatera's free tier lets you spin one up now.Start free on Kamatera → (opens in new tab)

What OpenObserve is

OpenObserve is an observability backend written in Rust and shipped as a single binary. It ingests logs, metrics, traces and frontend RUM events, stores them as compressed Parquet files, and lets you search them with SQL from its own web UI. It speaks the protocols your tools already produce. Logs arrive as JSON or through Elasticsearch-style bulk requests. Traces arrive over OTLP. Metrics arrive through Prometheus remote write. So Fluent Bit, Vector, the OpenTelemetry Collector or a Prometheus server can send to it without a custom agent.

For a single VPS, the selling point is that there is nothing else to run. There is no Elasticsearch cluster, no ClickHouse and no separate metadata database, just one container and one data directory. SigNoz vs OpenObserve compares it with the ClickHouse-based SigNoz. If you only need host metrics and dashboards, Prometheus is the narrower tool.

One design fact to know before you send anything: OpenObserve's README states that ingested data is immutable. Individual records cannot be modified or deleted. Data only goes away when it ages out of retention. Keep secrets and personal data out of your logs at the source, because you can't scrub them later.

Server sizing

  • RAM: our catalog lists 512 MB as the minimum. In our install check (GCP e2-standard-2, Ubuntu 26.04, September 2026), OpenObserve with no data idled at about 238 MB of RAM and took about 556 MB of Docker disk. Memory rises with ingest rate and with the size of the queries you run, so plan on 2 GB or more once real log volume arrives.
  • Disk: this is where the growth goes. Parquet compression keeps the data small, but volume and retention decide the total. Start with 40 GB of SSD and watch du for the first week.
  • CPU: one or two vCPUs handle a small fleet's logs comfortably. Heavy full-text searches over long time ranges are what consume CPU.

Keep it on its own box, apart from the services that send it data. The Best VPS for Monitoring page ranks hosts for this.

Prepare the server

This guide assumes Docker Engine and the Compose plugin are installed. If not, work through Docker & Compose on Ubuntu first. Open SSH and the reverse-proxy ports only:

sudo ufw allow OpenSSH
sudo ufw allow 80
sudo ufw allow 443
sudo ufw --force enable
sudo ufw status verbose

OpenObserve's HTTP port (5080) will be bound to 127.0.0.1 below. Docker's published ports bypass ufw, so the loopback bind is what actually keeps it private, not the firewall.

Where to host itaffiliate disclosure
Hetzner Cloudrun it on
2 vCPU · 4 GB RAM · 80 GB SSD · $23.59/mo
Get Hetzner Cloud (opens in new tab)
Kamaterafree trial
1 vCPU · 1 GB RAM · 20 GB SSD · $4.00/mo
Start free on Kamatera → (opens in new tab)
DigitalOceanalso works on
1 vCPU · 1 GB RAM · 25 GB SSD · $6.00/mo
Deploy on DigitalOcean → (opens in new tab)

Paid link — we earn a commission if you shop through it.

Generate the root credentials

OpenObserve creates its root user from two environment variables, ZO_ROOT_USER_EMAIL and ZO_ROOT_USER_PASSWORD, on first startup only. After that they're stored in its metadata and are no longer required. The password must be 8–128 characters with an uppercase letter, a lowercase letter, a digit and a symbol, or the server refuses to start. Don't reuse the Complexpass#123 example from the quick-start.

Put them in an .env file that only you can read:

mkdir -p ~/openobserve && cd ~/openobserve
if [ ! -f .env ]; then
  printf 'ZO_ROOT_USER_EMAIL=admin@example.com\nZO_ROOT_USER_PASSWORD=%s\n' \
    "$(openssl rand -hex 12)Aa1#" > .env
fi
chmod 600 .env
cut -d= -f1 .env

The generated password is 24 random hex characters with Aa1# appended, so the complexity rule is always met. Change the email to yours. It is your login. Read the password with cat ~/openobserve/.env when you first sign in.

Install OpenObserve (Docker Compose)

This is the open-source image from OpenObserve's public registry, pinned to a release so upgrades happen when you choose:

cd ~/openobserve
cat > docker-compose.yml <<'YAML'
services:
  openobserve:
    image: public.ecr.aws/zinclabs/openobserve:v1.0.4
    container_name: openobserve
    restart: unless-stopped
    env_file: .env
    environment:
      ZO_DATA_DIR: /data
      ZO_COMPACT_DATA_RETENTION_DAYS: "30"
      ZO_TELEMETRY: "false"
    ports:
      # Loopback only: Caddy is the public entry point.
      - "127.0.0.1:5080:5080"
    volumes:
      - openobserve-data:/data

volumes:
  openobserve-data:
YAML
docker compose up -d

Three settings are worth explaining:

  • ZO_COMPACT_DATA_RETENTION_DAYS: "30". The default is 3650 days, ten years. On a VPS disk that is a slow-motion outage, so set a number you can afford. The documented minimum is 3. You can also set retention per stream in the UI.
  • ZO_TELEMETRY: "false". OpenObserve sends anonymous usage telemetry by default. This turns it off.
  • ZO_DATA_DIR: /data. Parquet files and metadata live here, on a named volume that survives container recreation.

Wait for the health check, then prove ingestion works by sending two log lines to a stream called smoke in the default organisation:

cd ~/openobserve
for i in $(seq 1 30); do
  curl -fs http://127.0.0.1:5080/healthz && break
  sleep 2
done
echo
set -a; . ./.env; set +a
curl -s -u "$ZO_ROOT_USER_EMAIL:$ZO_ROOT_USER_PASSWORD" \
  http://127.0.0.1:5080/api/default/smoke/_json \
  -d '[{"level":"info","message":"hello from the VPS"},{"level":"warn","message":"second line"}]'
echo

A response listing the smoke stream with "successful":2 means OpenObserve accepted both records. Open the UI and they appear under Logs once you pick the smoke stream.

HTTPS + domain

Point an A record for logs.example.com at the server and put Caddy in front of the loopback port:

logs.example.com {
    reverse_proxy 127.0.0.1:5080
}

If alerts and links should carry the public URL, set ZO_WEB_URL (for example https://logs.example.com) in the environment: block and recreate the container. The docs describe it as the UI URL used for redirects and alert links.

Sending data

Every ingestion endpoint takes the organisation in the path (default unless you create others) and uses HTTP basic auth. The Data Sources page in the UI shows ready-made snippets with your credentials filled in. The endpoints you will use most:

  • Logs (JSON): POST /api/default/<stream>/_json, as in the smoke test.
  • Traces (OTLP over HTTP): POST /api/default/traces, the endpoint an OpenTelemetry Collector's otlphttp exporter targets.
  • Metrics: POST /api/default/prometheus/api/v1/write. Add it as a remote_write URL in an existing Prometheus.

Don't use the root account for shippers. Create a separate user for ingestion in the UI, so a leaked agent config doesn't hand out admin access. Senders on other machines should go through the HTTPS domain, never a published 5080.

Backups

Everything is in the openobserve-data volume: the Parquet data and the metadata (users, streams, dashboards, alerts). Stop the container for a consistent copy:

cd ~/openobserve
docker compose stop
docker run --rm -v openobserve_openobserve-data:/data -v "$PWD":/backup alpine \
  tar czf /backup/openobserve-$(date +%F).tar.gz -C /data .
docker compose start
ls -lh ~/openobserve/*.tar.gz

The volume name gets the compose project prefix (openobserve_). Check it with docker volume ls. Copy the archive off the box. For larger installs, OpenObserve can keep its data in S3-compatible object storage instead of local disk. That changes the backup story to the bucket's own versioning, so read the storage docs before relying on it.

Upgrades

Change the image tag in docker-compose.yml to the new release, back up, then:

cd ~/openobserve
docker compose pull
docker compose up -d

Read the release notes before a major version jump, and keep the backup until you've confirmed old data still searches correctly.

Troubleshooting

The container exits immediately on first start. Read docker compose logs openobserve. A password that fails the complexity rule is the usual cause. The server refuses to create the root user with it.

Login fails after you changed .env. The root credentials are only read on first startup. Editing .env later changes nothing. Change the password in the UI instead.

Ingest returns 401. Check the basic-auth credentials the shipper sends, and the organisation name in the path (/api/default/...).

The disk is filling faster than expected. Lower ZO_COMPACT_DATA_RETENTION_DAYS or set per-stream retention in the UI. Remember that retention only drops whole time ranges, and nothing inside them can be deleted selectively.

Verification + next steps

You're done when https://logs.example.com loads over a valid certificate, you can sign in as the root user, the smoke stream shows two records, and a backup archive sits somewhere off the box.

Next: point a log shipper at a real stream, create an ingestion-only user, and set per-stream retention for anything noisy. For metrics dashboards on top of Prometheus, see Grafana.

Next steps

How to self-host OpenObserve →More self-hosted observability tools →Automatic HTTPS with Caddy →Run Claude Code with Ollama on Your Own VPS →Deploy Coolify on a VPS →How to Deploy Actual Budget on a VPS →How to Deploy AnythingLLM on a VPS →How to Deploy Appwrite on a VPS →How to Deploy Audiobookshelf on a VPS →How to Deploy Authelia on a VPS →How to Deploy authentik on a VPS →How to Deploy Baserow on a VPS →How to Deploy Beszel on a VPS →How to Deploy Bitwarden on a VPS →How to Deploy BookStack on a VPS →How to Deploy CapRover on a VPS →How to Deploy Checkmate on a VPS →How to Deploy Directus on a VPS →How to Deploy docker-mailserver on a VPS →How to Deploy Docmost on a VPS →How to Deploy Dokku on a VPS →How to Deploy Dokploy on a VPS →How to Deploy Firefly III on a VPS →How to Deploy Forgejo on a VPS →How to Deploy Gatus on a VPS →How to Deploy Ghostfolio on a VPS →How to Deploy Gitea on a VPS →How to Deploy GitLab on a VPS →How to Deploy GlitchTip on a VPS →How to Deploy Grafana on a VPS →How to Deploy Graylog on a VPS →How to Deploy Headscale on a VPS →How to Deploy Healthchecks on a VPS →How to Deploy Home Assistant on a VPS →How to Deploy Immich on a VPS →How to Deploy Jan on a VPS →How to Deploy Jellyfin on a VPS →How to Deploy Karakeep on a VPS →How to Deploy Keycloak on a VPS →How to Deploy Leantime on a VPS →How to Deploy LibreChat on a VPS →How to Deploy Linkwarden on a VPS →How to Deploy LocalAI on a VPS →How to Deploy Mailcow on a VPS →How to Deploy Mailu on a VPS →How to Deploy Matomo on a VPS →How to Deploy Mattermost on a VPS →How to Deploy Meilisearch on a VPS →How to Deploy Memos on a VPS →How to Deploy n8n on a VPS →How to Deploy Navidrome on a VPS →How to Deploy NetBird on a VPS →How to Deploy Netdata on a VPS →How to Deploy Nextcloud on a VPS →How to Deploy Next.js to a VPS →How to Deploy Nginx Proxy Manager on a VPS →How to Deploy NocoDB on a VPS →How to Deploy ntfy on a VPS →How to Deploy Ollama on a VPS →How to Deploy Open WebUI on a VPS →How to Deploy OpenHands on a VPS →How to Deploy OpenProject on a VPS →How to Deploy Outline on a VPS →How to Deploy Pangolin on a VPS →How to Deploy Paperless-ngx on a VPS →How to Deploy Passbolt on a VPS →How to Deploy Plane on a VPS →How to Deploy Plausible Analytics on a VPS →How to Deploy Pocket ID on a VPS →How to Deploy PocketBase on a VPS →How to Deploy Prometheus on a VPS →How to Deploy Psono on a VPS →How to Deploy Radarr on a VPS →How to Deploy Rocket.Chat on a VPS →How to Deploy SigNoz on a VPS →How to Deploy Sonarr on a VPS →How to Deploy Stalwart on a VPS →How to Deploy Stirling-PDF on a VPS →How to Deploy Supabase on a VPS →How to Deploy Synapse on a VPS →How to Deploy Taiga on a VPS →How to Deploy TeamPass on a VPS →How to Deploy Tinyauth on a VPS →How to Deploy Traefik on a VPS →How to Deploy Trilium on a VPS →How to Deploy Twenty CRM on a VPS →How to Deploy Umami on a VPS →How to Deploy Uptime Kuma on a VPS →How to Deploy Vaultwarden on a VPS →How to Deploy Vikunja on a VPS →How to Deploy wg-easy on a VPS →How to Deploy Wiki.js on a VPS →How to Deploy Zabbix on a VPS →How to Deploy Zitadel on a VPS →How to Deploy Zulip on a VPS →Docker & Compose on Ubuntu 26.04 →Building AI Workflows with n8n →Install Open WebUI with Ollama →Adding AI-Powered Insights to Plausible Analytics →Building AI-Powered Apps with Supabase and pgvector →

We use analytics cookies (Google Analytics, PostHog) to see which guides are useful. No ad networks, no cross-site tracking. See our privacy policy.