Observability AWS

VictoriaLogs 1.53.0 on AWS User Guide

| Product: VictoriaLogs 1.53.0 on Ubuntu 24.04 on AWS

Overview

This guide covers launching and using the VictoriaLogs 1.53.0 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.

VictoriaLogs is a log database from VictoriaMetrics. It accepts structured and plain text logs over many protocols (its own JSON lines API, the Loki push API, the Elasticsearch bulk API, OpenTelemetry and syslog), stores them compressed on disk, and lets you search and aggregate them with the LogsQL query language through a built in web interface (vmui) or plain HTTP. It is a single binary that needs far less memory and disk than a typical Elasticsearch or OpenSearch stack.

An upstream VictoriaLogs binary listens on all interfaces with no authentication. The cloudimg image changes that:

  • HTTPS from the first boot. nginx is the only network entry point and serves the web UI and every ingest and query API on port 443 with a certificate generated on your instance's first boot. Port 80 only redirects to HTTPS.
  • A password unique to your instance. Every path except a static /healthz needs HTTP Basic Auth as the user admin, including the delete and internal APIs. The password is generated on first boot and written to the root only file /root/victorialogs-credentials.txt; nginx stores only its bcrypt hash. No password is baked into the image and no two instances share one.
  • VictoriaLogs on loopback only. Its HTTP port 9428 is bound to 127.0.0.1 and is never reachable from the network, so never open it in a security group. The syslog receiver is off.
  • Fail closed. nginx refuses to start until first boot has written the certificate and the password, so VictoriaLogs is never served without a login.
  • Your logs on their own disk. All log data lives on a dedicated 32 GiB EBS volume mounted at /var/lib/victoria-logs, which you can resize independently of the root volume.
  • Fixed host name and real client addresses. Requests for a host name the instance does not serve are dropped, nginx logs the real client address (client supplied X-Forwarded-For headers are overwritten), and cross site browser requests that change state are refused.
  • Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.

What is on the image: VictoriaLogs 1.53.0 (/usr/local/bin/victoria-logs-prod, the unmodified upstream community release binary, checksum verified, Apache-2.0), nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent. VictoriaLogs runs as its own unprivileged victorialogs user with a 30 day retention period by default.

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.
  • A security group that allows TCP 22 (SSH) from your own networks and TCP 443 (the web UI and the ingest and query APIs) from the networks your users and log agents are in. TCP 80 only redirects to HTTPS and is optional. If your agents run inside the same VPC, allow 443 from the VPC CIDR only.

Step 1: Launch the instance

In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick VictoriaLogs 1.53.0 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large, your key pair and a security group as above. The AMI brings its own 32 GiB data volume for the logs; increase its size on the launch page if you expect more log data (Step 9).

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=victorialogs}]'

Step 2: Connect to your instance

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

Step 3: Check the services

First boot generates the certificate and the password, then nginx starts in front of VictoriaLogs:

systemctl is-active victorialogs nginx victorialogs-firstboot
sudo test -f /var/lib/cloudimg/victorialogs-firstboot.done && echo FIRSTBOOT_DONE
/usr/local/bin/victoria-logs-prod -version
active
active
active
FIRSTBOOT_DONE
victoria-logs-20261001-105650-tags-v1.53.0-0-g915d91904b

Only SSH and nginx listen on public addresses; VictoriaLogs 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:9428 users:(("victoria-logs-p"))
[::]:22 users:(("sshd"),("systemd"))
[::]:443 users:(("nginx"),("nginx"),("nginx"))
[::]:80 users:(("nginx"),("nginx"),("nginx"))

Step 4: Get your URL and password

The URLs and the user name are in the root only credentials file:

sudo grep -E '^victorialogs\.(url|ingest_url|username)=' /root/victorialogs-credentials.txt
victorialogs.url=https://ec2-<public-ip-dashed>.compute-1.amazonaws.com/select/vmui/
victorialogs.ingest_url=https://ec2-<public-ip-dashed>.compute-1.amazonaws.com/insert/jsonline
victorialogs.username=admin

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

The URL 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, so browsers and agents can use any of them:

openssl x509 -in /etc/ssl/victorialogs/victorialogs.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

Without the password every path is refused, a wrong password is refused, and port 80 redirects to HTTPS. With the password the web UI answers, and /healthz answers without a login for load balancer health checks. The commands below read the password from the credentials file into a shell variable and pass it to curl through a file descriptor, so it never appears in the process list or your shell history:

H=$(sudo sed -n 's#^victorialogs\.url=https://\([^/]*\)/.*#\1#p' /root/victorialogs-credentials.txt)
PW=$(sudo sed -n 's/^victorialogs\.password=//p' /root/victorialogs-credentials.txt)
CA=/etc/ssl/victorialogs/victorialogs.crt
curl -s -o /dev/null -w 'http://  -> %{http_code} %{redirect_url}\n' --resolve "$H:80:127.0.0.1" "http://$H/select/vmui/"
curl -s -o /dev/null -w 'no login -> HTTP %{http_code}\n' --cacert $CA --resolve "$H:443:127.0.0.1" "https://$H/select/vmui/"
curl -s -o /dev/null -w 'wrong password -> HTTP %{http_code}\n' --cacert $CA --resolve "$H:443:127.0.0.1" -u admin:not-the-password "https://$H/select/vmui/"
curl -s -o /dev/null -w 'admin + password -> HTTP %{http_code}\n' --cacert $CA --resolve "$H:443:127.0.0.1" -K <(printf 'user = "admin:%s"\n' "$PW") "https://$H/select/vmui/"
curl -s -w ' <- /healthz (no login) HTTP %{http_code}\n' --cacert $CA --resolve "$H:443:127.0.0.1" "https://$H/healthz"
http://  -> 301 https://ec2-<public-ip-dashed>.compute-1.amazonaws.com/select/vmui/
no login -> HTTP 401
wrong password -> HTTP 401
admin + password -> HTTP 200
ok
 <- /healthz (no login) HTTP 200

--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 URL directly.

Step 6: Send logs and query them with LogsQL

The JSON lines API takes one JSON object per line. The query string names the field that holds the message (_msg_field), the timestamp field (_time_field, 0 means now) and the fields that identify a log stream (_stream_fields). Send twelve sample lines, then query them back:

H=$(sudo sed -n 's#^victorialogs\.url=https://\([^/]*\)/.*#\1#p' /root/victorialogs-credentials.txt)
PW=$(sudo sed -n 's/^victorialogs\.password=//p' /root/victorialogs-credentials.txt)
CA=/etc/ssl/victorialogs/victorialogs.crt
for svc in web-01 web-02 db-01; do
  for lvl in info info warn error; do
    printf '{"_msg":"%s request handled on %s","_time":"0","service":"checkout","host":"%s","level":"%s"}\n' "$lvl" "$svc" "$svc" "$lvl"
  done
done | curl -s -o /dev/null -w 'ingest -> HTTP %{http_code}\n' --cacert $CA --resolve "$H:443:127.0.0.1" \
  -K <(printf 'user = "admin:%s"\n' "$PW") -H 'Content-Type: application/stream+json' --data-binary @- \
  "https://$H/insert/jsonline?_stream_fields=service,host&_time_field=_time&_msg_field=_msg"
sleep 2
curl -s --cacert $CA --resolve "$H:443:127.0.0.1" -K <(printf 'user = "admin:%s"\n' "$PW") \
  --data-urlencode 'query=level:error | fields host, level, _msg | sort by (host)' \
  "https://$H/select/logsql/query"
curl -s --cacert $CA --resolve "$H:443:127.0.0.1" -K <(printf 'user = "admin:%s"\n' "$PW") \
  --data-urlencode 'query=service:checkout | stats by (level) count() as lines | sort by (level)' \
  "https://$H/select/logsql/query"
ingest -> HTTP 200
{"host":"db-01","level":"error","_msg":"error request handled on db-01"}
{"host":"web-01","level":"error","_msg":"error request handled on web-01"}
{"host":"web-02","level":"error","_msg":"error request handled on web-02"}
{"level":"error","lines":"3"}
{"level":"info","lines":"6"}
{"level":"warn","lines":"3"}

The first query filters on a field and picks the fields to return; the second counts lines per level with the stats pipe. The LogsQL documentation lists every filter and pipe.

VictoriaLogs also accepts the Loki push API, so agents already configured for Grafana Loki can send here unchanged:

H=$(sudo sed -n 's#^victorialogs\.url=https://\([^/]*\)/.*#\1#p' /root/victorialogs-credentials.txt)
PW=$(sudo sed -n 's/^victorialogs\.password=//p' /root/victorialogs-credentials.txt)
CA=/etc/ssl/victorialogs/victorialogs.crt
TS=$(date +%s%N)
curl -s -o /dev/null -w 'loki push -> HTTP %{http_code}\n' --cacert $CA --resolve "$H:443:127.0.0.1" \
  -K <(printf 'user = "admin:%s"\n' "$PW") -H 'Content-Type: application/json' \
  -d "{\"streams\":[{\"stream\":{\"job\":\"guide-test\"},\"values\":[[\"$TS\",\"hello from the Loki push API\"]]}]}" \
  "https://$H/insert/loki/api/v1/push"
sleep 2
curl -s --cacert $CA --resolve "$H:443:127.0.0.1" -K <(printf 'user = "admin:%s"\n' "$PW") \
  --data-urlencode 'query=job:guide-test | fields job, _msg' "https://$H/select/logsql/query"
loki push -> HTTP 204
{"job":"guide-test","_msg":"hello from the Loki push API"}

Step 7: Explore logs in the web UI

Open the victorialogs.url from Step 4 (https://<your-host>/select/vmui/) in a browser. The certificate is self signed, so the browser warns once; accept it, or install your own certificate (Step 8). Sign in as admin with the password from the credentials file.

Type a LogsQL query, for example service:checkout, choose a time range and press Execute. The hits histogram shows when the lines arrived, the Stream fields panel lists the fields that identify your log streams, and the results are grouped by stream with colour coded levels:

The VictoriaLogs vmui Query view running the LogsQL query service:checkout, showing the hits histogram, the stream fields panel and the colour coded log lines in Group view

Narrow the search with a filter such as level:error:

The VictoriaLogs vmui running the LogsQL filter level:error, showing only the three error lines and the updated histogram

The Table tab shows each line as a row with one column per field, and JSON shows the raw records:

The VictoriaLogs vmui Table view showing the error lines as rows with time, message, host and service columns

The Overview page summarises everything stored: total lines, ingestion rate, the number of log streams, and the fields with how many lines carry each one:

The VictoriaLogs vmui Overview page showing total logs, logs per second, unique log streams, the hits histogram and the field names table

Step 8: Send logs from your servers

Any agent that speaks one of the supported APIs works. Give it the URL, the user admin, the password, and the instance's certificate so it can verify the connection. Copy the certificate to each agent host first (it is public, not a secret):

scp -i <your-key.pem> ubuntu@<victorialogs-host>:/etc/ssl/victorialogs/victorialogs.crt ./victorialogs.crt

Fluent Bit ([OUTPUT] section), sending JSON lines:

[OUTPUT]
    Name             http
    Match            *
    Host             <victorialogs-host>
    Port             443
    URI              /insert/jsonline?_stream_fields=stream,host&_msg_field=log&_time_field=date
    Format           json_lines
    Json_date_format iso8601
    tls              On
    tls.verify       On
    tls.ca_file      /etc/fluent-bit/victorialogs.crt
    http_User        admin
    http_Passwd      <VICTORIALOGS_PASSWORD>

Vector (vector.yaml), using the Elasticsearch compatible API:

sinks:
  victorialogs:
    type: elasticsearch
    inputs: [your_source]
    endpoints: ["https://<victorialogs-host>/insert/elasticsearch/"]
    api_version: v8
    compression: gzip
    healthcheck:
      enabled: false
    auth:
      strategy: basic
      user: admin
      password: <VICTORIALOGS_PASSWORD>
    tls:
      ca_file: /etc/vector/victorialogs.crt
    query:
      _msg_field: message
      _time_field: timestamp
      _stream_fields: host,container_name

Grafana Alloy, Promtail or any Loki client: use https://<victorialogs-host>/insert/loki/api/v1/push as the push URL with basic auth admin and the password. OpenTelemetry Collector: use an otlphttp exporter with logs_endpoint: https://<victorialogs-host>/insert/opentelemetry/v1/logs and a basicauth extension.

Keep the password in the agent's own secret store or a root only file rather than in a world readable configuration. Agents send no browser Origin header, so the image's cross site request guard does not affect them.

Syslog (optional). The syslog receiver is off because syslog has no authentication. To receive syslog from hosts in your own network, add -syslog.listenAddr.tcp=:514 to the ExecStart line of /etc/systemd/system/victorialogs.service, run sudo systemctl daemon-reload && sudo systemctl restart victorialogs, and open TCP 514 in the security group only to your VPC or VPN CIDRs, never to 0.0.0.0/0. Senders then use the instance's private IP address.

Step 9: Storage and retention

VictoriaLogs keeps all log data on the dedicated EBS volume mounted at /var/lib/victoria-logs:

sudo du -sh /var/lib/victoria-logs
df -h /var/lib/victoria-logs | tail -1 | awk '{print $2" total, "$4" free on "$6}'
systemctl cat victorialogs | grep -E '^\s+-'
156K    /var/lib/victoria-logs
32G total, 30G free on /var/lib/victoria-logs
  -httpListenAddr=127.0.0.1:9428 \
  -storageDataPath=/var/lib/victoria-logs \
  -retentionPeriod=30d \
  -loggerTimezone=UTC

Logs older than the retention period (30 days) are deleted automatically. To keep them longer, change -retentionPeriod (for example -retentionPeriod=90d or -retentionPeriod=1y) in /etc/systemd/system/victorialogs.service, then run sudo systemctl daemon-reload && sudo systemctl restart victorialogs. You can also cap the disk space with -retention.maxDiskSpaceUsageBytes.

To grow the volume, modify it in the EC2 console (Volumes, Modify volume) and then extend the file system online:

sudo resize2fs "$(findmnt -no SOURCE /var/lib/victoria-logs)"

Take EBS snapshots of the data volume for backups.

Step 10: Use your own DNS name or certificate

The served host name is fixed by configuration, never taken from a request. To serve VictoriaLogs under your own DNS name, point the name at the instance and run the helper, which records the name, regenerates the self signed certificate for it, updates the credentials file and reloads nginx:

sudo victorialogs-set-hostname <your-dns-name>

Run the same command after a stop and start if the instance's public DNS name changed (an Elastic IP avoids that), because a request for a name the instance does not serve is dropped.

To use a certificate from your own CA or a public CA instead, replace /etc/ssl/victorialogs/victorialogs.crt (full chain) and /etc/ssl/victorialogs/victorialogs.key (mode 0600, owner root), then run sudo nginx -t && sudo systemctl reload nginx. Alternatively, put the instance behind an Application Load Balancer with an AWS Certificate Manager certificate and let the load balancer forward to port 443, with /healthz as the health check path.

Step 11: Security notes

VictoriaLogs runs as its own unprivileged user with a read only system:

systemctl show victorialogs -p User -p NoNewPrivileges -p ProtectSystem -p ProtectHome
User=victorialogs
ProtectHome=yes
ProtectSystem=full
NoNewPrivileges=yes

A state changing request carrying a foreign browser Origin is refused, even with the right password, so a web page cannot write to or delete from VictoriaLogs through a signed in browser:

H=$(sudo sed -n 's#^victorialogs\.url=https://\([^/]*\)/.*#\1#p' /root/victorialogs-credentials.txt)
PW=$(sudo sed -n 's/^victorialogs\.password=//p' /root/victorialogs-credentials.txt)
CA=/etc/ssl/victorialogs/victorialogs.crt
curl -s -o /dev/null -w 'Origin evil.example ingest -> HTTP %{http_code}\n' --cacert $CA --resolve "$H:443:127.0.0.1" -K <(printf 'user = "admin:%s"\n' "$PW") -H 'Origin: https://evil.example' -H 'Content-Type: application/json' -d '{"_msg":"x"}' "https://$H/insert/jsonline"
Origin evil.example ingest -> HTTP 403

Other points:

  • VictoriaLogs has a single account: anyone with the password can ingest, read and delete every log stream. Give each team its own instance if they must not see each other's logs.
  • nginx logs the real client address of every request; X-Forwarded-For headers sent by clients are ignored.
  • The AMI requires IMDSv2. Restrict port 443 to the networks your users and agents use, and never open 9428.
  • To rotate the password, generate a new one, write its bcrypt hash with sudo htpasswd -iB /etc/nginx/.victorialogs.htpasswd admin (it reads the new password from standard input), update /root/victorialogs-credentials.txt, reload nginx and update your agents.

Support and licensing

VictoriaLogs is open source under the Apache License 2.0; the image ships the unmodified upstream community binary (not the enterprise edition) and 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

VictoriaLogs is developed by VictoriaMetrics; for features and LogsQL, see the upstream VictoriaLogs documentation.

VictoriaLogs and VictoriaMetrics are trademarks of VictoriaMetrics, Inc. cloudimg is not affiliated with or endorsed by VictoriaMetrics; the names are used only to identify the unmodified upstream software.