Observability AWS

OpenObserve 1.1.2 on AWS User Guide

| Product: OpenObserve 1.1.2 on Ubuntu 24.04 on AWS

Overview

This guide covers launching and using the OpenObserve 1.1.2 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg log management listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.

OpenObserve is an open source observability platform for logs, metrics and traces. Agents send data over HTTP (JSON, Elasticsearch bulk or OpenTelemetry OTLP), and you search it with SQL or PromQL, build dashboards and set alerts in the built in web interface. It is a single self contained binary that stores everything on local disk, so a single instance needs no external database or object store.

The cloudimg image adds:

  • HTTPS from the first boot. nginx is the only network entry point and serves the web interface and every ingest API on port 443 with a certificate generated on your instance's first boot. Port 80 only redirects to HTTPS.
  • A root password unique to your instance. OpenObserve creates its root user on the first start of an empty store. The image ships an empty store and no password: first boot generates the password, writes it to the root only file /root/openobserve-credentials.txt and only then starts OpenObserve. No two instances share a password, and OpenObserve will not start before first boot has run.
  • OpenObserve on loopback only. OpenObserve's HTTP API listens on 127.0.0.1:5080 and its gRPC port on 127.0.0.1:5081. Neither is reachable from the network; agents use OTLP over HTTPS on port 443.
  • Fail closed. nginx refuses to start until first boot has written the certificate and the password.
  • Hardened service. OpenObserve runs as an unprivileged openobserve user in a systemd sandbox and cannot reach the EC2 instance metadata service, so an alert webhook or URL import cannot be turned against the instance's credentials. Requests for a host name the instance does not serve are dropped, cross site requests and WebSocket upgrades are refused, session cookies are marked Secure, OpenObserve's own metrics endpoint is not exposed, and nginx logs the real client address rather than any X-Forwarded-For header a client sends.
  • Data on its own volume. Everything OpenObserve stores lives on a separate 20 GiB EBS volume mounted at /var/lib/openobserve, which you can grow independently of the system disk.
  • Key only SSH. Password logins, root logins and X11 forwarding are switched off.
  • Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.

What is on the image: OpenObserve 1.1.2, the unmodified upstream open source build (/usr/local/bin/openobserve, GNU AGPL v3.0), nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent.

Default user: ubuntu · Architecture: x86_64 · Recommended instance type: m5.large

Prerequisites

  • An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet with a route to the internet (or a private route you use for management).
  • A security group that allows TCP 22 (SSH), TCP 443 (web interface and ingest) and TCP 80 (redirects to HTTPS) from your own networks and the hosts that send data only. Never open ports 5080 or 5081; OpenObserve listens on loopback.

Step 1: Launch the instance

In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick OpenObserve 1.1.2 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large or larger (OpenObserve caches recent data in memory, so give busy instances more RAM), your key pair and a security group as above. The AMI includes a 20 GiB data volume; increase its size on the Configure storage step if you expect to keep a lot of data.

With the AWS CLI (replace the values in angle brackets):

aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
  --instance-type m5.large --key-name <your-key-pair> \
  --security-group-ids <your-security-group> --subnet-id <your-subnet> \
  --metadata-options HttpTokens=required,HttpEndpoint=enabled \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=openobserve}]'

Step 2: Connect to your instance

Version SSH user
OpenObserve 1.1.2 on Ubuntu 24.04 ubuntu
ssh -i <your-key.pem> ubuntu@<public-ip>

The login message shows the web address and where to find the password.

Step 3: Check the services

First boot takes a few seconds: it generates the password and the certificate, starts OpenObserve on an empty store, and then nginx starts in front of it:

systemctl is-active openobserve-firstboot openobserve nginx
sudo test -f /var/lib/cloudimg/openobserve-firstboot.done && echo FIRSTBOOT_DONE
/usr/local/bin/openobserve --version
curl -s http://127.0.0.1:5080/config | jq -r '"build_type=\(.build_type) commit=\(.commit_hash)"'
active
active
active
FIRSTBOOT_DONE
openobserve v1.1.2
build_type=opensource commit=d16d693ba99bc20621a58b948ecbcad7ec7fa882

build_type=opensource and the commit of the upstream v1.1.2 tag confirm the open source build.

Only SSH and nginx listen on public addresses; OpenObserve listens on loopback:

sudo ss -Hlntp | awk '{print $4, $6}' | grep -v '127.0.0.5' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g' | sort
0.0.0.0:22 users:(("sshd"),("systemd"))
0.0.0.0:443 users:(("nginx"),("nginx"),("nginx"))
0.0.0.0:80 users:(("nginx"),("nginx"),("nginx"))
127.0.0.1:5080 users:(("openobserve"))
127.0.0.1:5081 users:(("openobserve"))
[::]:22 users:(("sshd"),("systemd"))
[::]:443 users:(("nginx"),("nginx"),("nginx"))
[::]:80 users:(("nginx"),("nginx"),("nginx"))

The data lives on its own EBS volume:

findmnt -no TARGET,SOURCE,FSTYPE /var/lib/openobserve
df -h --output=size,avail,target /var/lib/openobserve
/var/lib/openobserve /dev/nvme1n1 ext4
 Size Avail Mounted on
  20G   19G /var/lib/openobserve

SSH accepts keys only: passwords, root logins and X11 forwarding are switched off in /etc/ssh/sshd_config.d/00-cloudimg.conf, which sorts before every other SSH setting. Check the effective values:

sudo sshd -T | grep -E '^(passwordauthentication|kbdinteractiveauthentication|permitrootlogin|x11forwarding|pubkeyauthentication) '
permitrootlogin no
pubkeyauthentication yes
passwordauthentication no
kbdinteractiveauthentication no
x11forwarding no

Step 4: Get your address and password

The address, user name and ingest URL are in the root only file /root/openobserve-credentials.txt:

sudo grep -E '^OPENOBSERVE_(URL|USERNAME|INGEST_URL)=' /root/openobserve-credentials.txt
OPENOBSERVE_URL=https://ec2-<public-ip-dashed>.compute-1.amazonaws.com/
OPENOBSERVE_USERNAME=admin@openobserve.local
OPENOBSERVE_INGEST_URL=https://ec2-<public-ip-dashed>.compute-1.amazonaws.com/api/default/<stream>/_json

The password is the OPENOBSERVE_PASSWORD line of the same file: sudo grep '^OPENOBSERVE_PASSWORD=' /root/openobserve-credentials.txt. Store it in your password manager; it is unique to this instance.

The address uses the instance's public DNS name from EC2 instance metadata (IMDSv2), or its public or private IPv4 address when it has no public name. The certificate names that host plus the instance's public and private IP addresses:

openssl x509 -in /etc/ssl/openobserve/openobserve.crt -noout -subject -ext subjectAltName -enddate
subject=CN = ec2-<public-ip-dashed>.compute-1.amazonaws.com, O = cloudimg
X509v3 Subject Alternative Name:
    DNS:ec2-<public-ip-dashed>.compute-1.amazonaws.com, IP Address:<public-ip>, IP Address:<private-ip>, IP Address:127.0.0.1, DNS:localhost
notAfter=<date about 27 months after first boot>

Step 5: Check the login

Port 80 redirects to HTTPS, /healthz answers without a login, the API refuses a missing or wrong password and accepts yours, and a request for a host name the instance does not serve is dropped. The commands read the password into a shell variable and hand it to curl through a file descriptor, so it never appears in the process list or your shell history:

H=$(sudo sed -n 's#^OPENOBSERVE_URL=https://\([^/]*\)/.*#\1#p' /root/openobserve-credentials.txt)
U=$(sudo sed -n 's/^OPENOBSERVE_USERNAME=//p' /root/openobserve-credentials.txt)
PW=$(sudo sed -n 's/^OPENOBSERVE_PASSWORD=//p' /root/openobserve-credentials.txt)
CA=/etc/ssl/openobserve/openobserve.crt
curl -s -o /dev/null -w 'http://        -> %{http_code} %{redirect_url}\n' --resolve "$H:80:127.0.0.1" "http://$H/"
curl -s -w ' <- healthz, HTTP %{http_code}\n' --cacert $CA --resolve "$H:443:127.0.0.1" "https://$H/healthz"
curl -s -o /dev/null -w 'no login       -> HTTP %{http_code}\n' --cacert $CA --resolve "$H:443:127.0.0.1" "https://$H/api/default/streams"
curl -s -o /dev/null -w 'wrong password -> HTTP %{http_code}\n' --cacert $CA --resolve "$H:443:127.0.0.1" -u "$U:not-the-password" "https://$H/api/default/streams"
curl -s -o /dev/null -w 'your password  -> HTTP %{http_code}\n' --cacert $CA --resolve "$H:443:127.0.0.1" -K <(printf 'user = "%s:%s"\n' "$U" "$PW") "https://$H/api/default/streams"
curl -s -o /dev/null -w 'metrics        -> HTTP %{http_code}\n' --cacert $CA --resolve "$H:443:127.0.0.1" "https://$H/metrics"
curl -sk -o /dev/null -w 'other host     -> HTTP %{http_code} (000 = connection dropped)\n' -H 'Host: evil.example' https://127.0.0.1/ || true
http://        -> 301 https://ec2-<public-ip-dashed>.compute-1.amazonaws.com/
{"status":"ok"} <- healthz, HTTP 200
no login       -> HTTP 401
wrong password -> HTTP 401
your password  -> HTTP 200
metrics        -> HTTP 404
other host     -> HTTP 000 (000 = connection dropped)

--resolve sends the request to this instance while keeping the real host name, so the certificate check passes. From another machine, drop --resolve and use the address directly.

Step 6: Sign in to the web interface

Open the OPENOBSERVE_URL address from Step 4 in your browser. The certificate is self signed (made on your instance), so your browser asks you to accept it once; Step 10 shows how to use your own name and certificate. Sign in with the email admin@openobserve.local and your password.

The OpenObserve sign in page

Logs lists your log streams. Pick a stream and a time range to see a histogram of events by level and the matching records; click a field on the left to add it to the view.

The Logs view of a stream with its histogram

Type a filter such as level='error' in the query bar and choose Run query, or switch on SQL mode to run full SQL over the stream:

The stream filtered to error events

Step 7: Send your first logs and query them

Every ingest and search API is under https://<your-host>/api/<organisation>/... and uses HTTP Basic Auth over HTTPS. The instance starts with the organisation default. This sends two JSON log lines to a stream called app_logs (OpenObserve creates the stream on first write) and then searches it with SQL:

H=$(sudo sed -n 's#^OPENOBSERVE_URL=https://\([^/]*\)/.*#\1#p' /root/openobserve-credentials.txt)
U=$(sudo sed -n 's/^OPENOBSERVE_USERNAME=//p' /root/openobserve-credentials.txt)
PW=$(sudo sed -n 's/^OPENOBSERVE_PASSWORD=//p' /root/openobserve-credentials.txt)
CA=/etc/ssl/openobserve/openobserve.crt
AUTH() { printf 'user = "%s:%s"\n' "$U" "$PW"; }
curl -s --cacert $CA --resolve "$H:443:127.0.0.1" -K <(AUTH) -H 'Content-Type: application/json' \
  -d '[{"level":"info","service":"checkout","message":"order placed","order_id":1001},
       {"level":"error","service":"payments","message":"card declined","order_id":1002}]' \
  "https://$H/api/default/app_logs/_json"; echo
sleep 3
NOW=$(date +%s%6N); FROM=$((NOW - 3600000000))
curl -s --cacert $CA --resolve "$H:443:127.0.0.1" -K <(AUTH) -H 'Content-Type: application/json' \
  -d "{\"query\":{\"sql\":\"SELECT level, service, message FROM \\\"app_logs\\\" WHERE level = 'error'\",\"start_time\":$FROM,\"end_time\":$NOW,\"from\":0,\"size\":10}}" \
  "https://$H/api/default/_search" | jq -c '{total, hits: [.hits[] | {level, service, message}]}'
{"code":200,"status":[{"name":"app_logs","successful":2,"failed":0}]}
{"total":1,"hits":[{"level":"error","service":"payments","message":"card declined"}]}

Search times are in microseconds since the epoch. The same data appears in Logs in the web interface under the app_logs stream.

Step 8: Connect your agents

Point log shippers and OpenTelemetry collectors at the instance's HTTPS address. Useful endpoints (organisation default):

Data Endpoint
JSON logs https://<your-host>/api/default/<stream>/_json
Elasticsearch bulk (Fluent Bit, Vector, Logstash) https://<your-host>/api/default/_bulk
OTLP over HTTP: logs, traces, metrics https://<your-host>/api/default/v1/logs, /v1/traces, /v1/metrics
Prometheus remote write https://<your-host>/api/default/prometheus/api/v1/write

For example, an OpenTelemetry Collector exporter:

exporters:
  otlphttp/openobserve:
    endpoint: https://<your-host>/api/default
    headers:
      Authorization: Basic <base64 of user-email:password>
    tls:
      ca_file: /etc/otel/openobserve.crt   # or insecure_skip_verify: true while you test

OTLP over gRPC (port 5081) is not exposed; use OTLP over HTTPS as above. Do not give agents the root login: in the web interface open IAM > Users, add a user with a role limited to ingestion, and use that user's email and password (or its token from Data sources) in your agents. Copy /etc/ssl/openobserve/openobserve.crt to your agents so they can verify the self signed certificate, or install a certificate from your own authority (Step 10). Open port 443 in the security group to the agents' addresses.

Step 9: Check the security settings

OpenObserve cannot reach the EC2 instance metadata service, so an alert destination or URL import cannot be used to read instance credentials; the root password is only in root only files:

systemctl show -p IPAddressDeny --value openobserve
sudo stat -c '%a %U %n' /etc/openobserve/root-user.env /root/openobserve-credentials.txt /etc/ssl/openobserve/openobserve.key
cat /etc/openobserve/web-url.env
169.254.169.254/32 fd00:ec2::254/128 168.63.129.16/32
600 root /etc/openobserve/root-user.env
600 root /root/openobserve-credentials.txt
600 root /etc/ssl/openobserve/openobserve.key
ZO_WEB_URL=https://ec2-<public-ip-dashed>.compute-1.amazonaws.com

ZO_WEB_URL is the address OpenObserve uses in links (for example in alert messages). It comes from the configured host name, never from a request.

Step 10: Use your own DNS name, certificate or password

The served host name comes from configuration, never from a request. To serve OpenObserve under your own DNS name, point the name at the instance and run the helper, which records the name, makes a new self signed certificate for it, updates ZO_WEB_URL and /root/openobserve-credentials.txt, reloads nginx and restarts OpenObserve:

sudo openobserve-set-hostname logs.example.com

To use a certificate from your own certificate authority instead, put the full chain in /etc/ssl/openobserve/openobserve.crt and the private key in /etc/ssl/openobserve/openobserve.key (owner root, mode 0600), then run sudo nginx -t && sudo systemctl reload nginx. Running openobserve-set-hostname again replaces them with a self signed pair.

To change the root password, sign in, open IAM > Users, edit admin@openobserve.local and set a new password, then store it in your password manager and update /root/openobserve-credentials.txt. The ZO_ROOT_USER_PASSWORD value in /etc/openobserve/root-user.env is only used when OpenObserve creates the root user on an empty store, so changing it later has no effect on an existing instance.

Step 11: Storage, backups and operations

  • All data (ingested logs, metrics and traces, plus the metadata database with users, dashboards and alerts) is under /var/lib/openobserve on its own EBS volume. Grow it with Modify volume in the EC2 console, then sudo resize2fs <device> (find the device with findmnt /var/lib/openobserve).
  • Back up with EBS snapshots of the data volume; for a consistent copy, stop OpenObserve first (sudo systemctl stop openobserve) or snapshot both volumes together.
  • Data is kept for 3,650 days by default. Set a shorter retention per stream in its settings under Data > Streams, or for the whole instance with ZO_COMPACT_DATA_RETENTION_DAYS=<days> in /etc/openobserve/openobserve.env followed by sudo systemctl restart openobserve, so the volume does not fill.
  • Manage the services with systemctl status openobserve nginx and read their logs with journalctl -u openobserve -u nginx. Unattended upgrades keep Ubuntu and nginx patched; check the listing for newer OpenObserve versions.

Step 12: Security notes

  • Restrict ports 443 and 80 to the networks your team and your agents use; ports 5080 and 5081 are never exposed.
  • Give each agent or team its own user with the least access it needs instead of sharing the root login.
  • SSH accepts key logins as ubuntu only (no passwords, no root login, no X11 forwarding); use sudo for administration.
  • The AMI requires IMDSv2. nginx logs the real client address of every request and ignores X-Forwarded-For headers sent by clients.
  • The web interface shows an offer for OpenObserve's commercial edition; this image runs the open source edition and works without it.

Support and licensing

OpenObserve is open source under the GNU Affero General Public License v3.0. The image runs the unmodified upstream v1.1.2 release; the licence and a note with the release checksums and a link to the corresponding source code are in /usr/share/doc/openobserve/. There is no per user or per CPU licence fee; the AMI price covers cloudimg's hardening, first boot setup and support.

  • Email: support@cloudimg.co.uk
  • Website: https://www.cloudimg.co.uk

OpenObserve is developed by OpenObserve Inc.; for features, see the upstream documentation at https://openobserve.ai/docs/.

OpenObserve is a trademark of OpenObserve Inc. cloudimg is not affiliated with or endorsed by OpenObserve Inc.; the name is used only to identify the upstream software.