Observability AWS

Grafana Alloy 1.21.3 on AWS User Guide

| Product: Grafana Alloy 1.21.3 on Ubuntu 24.04 on AWS

Overview

This guide covers launching and using the Grafana Alloy 1.21.3 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg tracing and APM listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.

Grafana Alloy is an open source telemetry collector from Grafana Labs: a vendor-neutral distribution of the OpenTelemetry Collector with built-in Prometheus pipelines. Applications and other collectors send it traces, metrics and logs over OTLP; Alloy batches, filters, enriches and routes them to the backends you choose (Grafana Tempo, Mimir, Loki, Prometheus, any OTLP endpoint or Grafana Cloud). Its web UI draws the pipeline as a graph of components, shows each component's health, arguments and exports, and lets you watch live data pass through a component.

What the cloudimg image adds:

  • No default login, for people and for applications. The Alloy UI signs in with the user admin and a password generated on your instance's first boot. Applications and collectors send telemetry with a second, separate login, otlp, which can only post OTLP data and cannot open the UI. nginx keeps only bcrypt hashes of both; the passwords are written to /var/lib/cloudimg/alloy-credentials.txt (root only), and the image ships no password and no TLS key.
  • OTLP/HTTP ingest on port 443, behind its login. nginx accepts POST /otlp/v1/traces, /otlp/v1/metrics and /otlp/v1/logs over TLS with the otlp login and forwards them to Alloy's receiver; nothing else on Alloy is reachable from the network. OTLP over gRPC stays on the loopback address unless you open it (Step 8).
  • Alloy on localhost only. The Alloy UI and API (12345) and the OTLP receivers (4317 gRPC, 4318 HTTP) listen on 127.0.0.1, and a loopback guard lets only nginx, Alloy, root and administrators connect to the UI port.
  • HTTPS from the first boot. The certificate is generated on your instance for its own names and addresses, port 80 redirects to HTTPS, and requests for a host name the instance does not serve are dropped. Cross-site and cross-origin requests to the UI are refused and failed logins are rate limited.
  • No phone-home, no remote configuration, no surprise egress. Alloy runs with usage reporting switched off (--disable-reporting), with no remotecfg block, no support bundle and no profiling endpoint. Alloy and nginx are blocked from the instance metadata service, the AMI requires IMDSv2, and the alloy account cannot open connections to private or link-local networks unless you allow a range (Step 9).
  • Fail closed. The services start only after first boot has finished, and a pre-start check refuses to start Alloy or nginx when a login, the certificate, the egress backstop or a file mode is missing or wrong, rather than starting it exposed.
  • Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on. Alloy is built from the source of the v1.21.3 tag (checksum pinned) with every dependency checked against Go's vulnerability database, recorded in the build record.

What is on the image: Grafana Alloy 1.21.3 (a single binary, /usr/local/bin/alloy, with its web UI built in), nginx 1.24 and OpenSSL 3.0 (from Ubuntu 24.04), cloud-init and the AWS Systems Manager agent. The shipped pipeline receives OTLP and writes it to a local file, so you can see telemetry arrive before you point Alloy at a backend (Step 7).

Default user: ubuntu · Architecture: x86_64 · Recommended instance type: m5.large (t3.medium is enough for light traffic)

The Alloy UI component list with the OTLP receiver, the batch processor and the file exporter, all healthy

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) from your admin network only;
  • TCP 443 (the Alloy UI and OTLP ingest) from the people who use the UI and from the applications and collectors that send telemetry. Senders in the same VPC can use the instance's private address;
  • TCP 80 only if you want plain HTTP requests redirected to HTTPS. You can leave it closed.
  • Never open 12345, 4317 or 4318. They listen on the loopback address only, and senders do not need them: on this image they send to port 443.

Step 1: Launch the instance

In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick Grafana Alloy 1.21.3 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large (or another type that fits your traffic), your key pair and a security group as above. Alloy keeps its write-ahead log and the shipped pipeline's output file under /var/lib/alloy on the root volume; 20 GiB is a sensible start.

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> \
  --block-device-mappings 'DeviceName=/dev/sda1,Ebs={VolumeSize=20,VolumeType=gp3}' \
  --metadata-options HttpTokens=required,HttpEndpoint=enabled \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=alloy}]'

First boot takes a few seconds once the instance is running; Alloy and nginx start straight after it.

Step 2: Connect to your instance

Version SSH user
Grafana Alloy 1.21.3 on Ubuntu 24.04 ubuntu
ssh -i <your-key.pem> ubuntu@<public-ip>

Step 3: Check the services

First boot generates the two passwords and the certificate, then Alloy and nginx start:

systemctl is-active alloy nginx alloy-firstboot cloudimg-alloy-egress
sudo test -f /var/lib/cloudimg/alloy-firstboot.done && echo FIRSTBOOT_DONE

All four units are active and the first boot marker is present:

active
active
active
active
FIRSTBOOT_DONE

Only SSH and nginx listen on network ports (one nginx entry per nginx process: the master and one worker per vCPU); Alloy is on the loopback address:

sudo ss -lntp | awk 'NR>1 {print $4, $6}' | grep -v ':53 ' | 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:12345 users:(("alloy"))
127.0.0.1:4317 users:(("alloy"))
127.0.0.1:4318 users:(("alloy"))
[::]:22 users:(("sshd"),("systemd"))
[::]:443 users:(("nginx"),("nginx"),("nginx"))
[::]:80 users:(("nginx"),("nginx"),("nginx"))

The health check nginx serves without a login, then Alloy's own readiness check on the loopback address (reachable from your administrator shell):

curl -sk https://127.0.0.1/healthz
sudo curl -s http://127.0.0.1:12345/-/ready
ok
Alloy is ready.

Step 4: Get your URL and logins

sudo cat /var/lib/cloudimg/alloy-credentials.txt

The file is readable by root only:

# Grafana Alloy 1.21.3 - generated on first boot by alloy-firstboot.service. Both passwords are unique to this
# instance; neither was baked into the image. Keep this file secret.

# The Alloy UI (component graph, health, live debugging): HTTP Basic login
ALLOY_UI_URL=https://<public-dns-name>/
ALLOY_UI_USER=admin
ALLOY_UI_PASSWORD=<ALLOY_UI_PASSWORD>
#
# OTLP/HTTP ingest for instrumented applications and OpenTelemetry exporters (HTTP Basic login 'otlp'):
#   POST ${OTLP_HTTP_ENDPOINT}/v1/traces | /v1/metrics | /v1/logs
# OpenTelemetry SDKs: OTEL_EXPORTER_OTLP_ENDPOINT=<OTLP_HTTP_ENDPOINT>, OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf,
# OTEL_EXPORTER_OTLP_HEADERS=Authorization=<OTLP_AUTHORIZATION_HEADER, with the space as %20>. The certificate is
# self-signed: give exporters OTLP_CA_CERTIFICATE (OTEL_EXPORTER_OTLP_CERTIFICATE) or use your own certificate.
OTLP_HTTP_ENDPOINT=https://<public-dns-name>/otlp
OTLP_USER=otlp
OTLP_PASSWORD=<OTLP_PASSWORD>
OTLP_AUTHORIZATION_HEADER=<OTLP_AUTHORIZATION_HEADER>
OTLP_CA_CERTIFICATE=/etc/ssl/alloy/alloy.crt
#
# Alloy's configuration: /etc/alloy/config.alloy (reload: sudo systemctl reload alloy). Received telemetry is written to
# /var/lib/alloy/otlp/telemetry.json until you point the pipeline at your own backend.
# New UI password: sudo alloy-ui-password   New ingest password: sudo alloy-otlp-password
# Self-test: sudo alloy-selftest   Change the served name: sudo alloy-set-hostname <your-domain>
# User guide: https://www.cloudimg.co.uk/guides/grafana-alloy-1-21-3-on-ubuntu-24-04-aws/

OTLP_AUTHORIZATION_HEADER is the ready-made Basic ... value of the otlp login for exporters that take a header rather than a user name and password.

The UI is served as the instance's public DNS name from the EC2 instance metadata; the certificate also names the public and private IP addresses and the private DNS name, and UI requests to the IP addresses redirect to the name (OTLP ingest is accepted on the IP addresses too, so senders inside your VPC can use the private address). If you stop and start the instance without an Elastic IP, the boot service picks up the new name and issues a new certificate. To serve Alloy under your own DNS name, see Step 10.

Open the URL in your browser. It warns once about the self-signed certificate; accept it (or install your own certificate, Step 10), then sign in as admin with the password from the credentials file when the browser asks. The UI opens on the list of components in the pipeline (the screenshot at the top of this guide). Graph draws how they connect, from the OTLP receiver through the batch processor to the file exporter:

The Alloy UI graph view with otelcol.receiver.otlp feeding otelcol.processor.batch feeding otelcol.exporter.file

Step 5: See it work with a test trace

Before you connect your own applications, send one span the way an OpenTelemetry SDK does: OTLP/HTTP with JSON, through nginx on port 443 with the otlp login. The login is read from the credentials file into a header file descriptor, so it never appears on a command line, then the span is read back from the file the shipped pipeline writes:

TRACE_ID="$(openssl rand -hex 16)"; SPAN_ID="$(openssl rand -hex 8)"; NOW="$(date +%s%N)"
cat > /tmp/alloy-span.json <<EOF
{"resourceSpans":[{"resource":{"attributes":[{"key":"service.name","value":{"stringValue":"checkout"}}]},
 "scopeSpans":[{"scope":{"name":"cloudimg-guide"},"spans":[{"traceId":"${TRACE_ID}","spanId":"${SPAN_ID}",
 "name":"GET /cart","kind":2,"startTimeUnixNano":"$((NOW - 150000000))","endTimeUnixNano":"${NOW}"}]}]}]}
EOF
curl -sk -o /dev/null -w 'HTTP %{http_code}\n' -X POST -H 'Content-Type: application/json' \
  -H @<(sudo sed -n 's/^OTLP_AUTHORIZATION_HEADER=/Authorization: /p' /var/lib/cloudimg/alloy-credentials.txt) \
  --data-binary @/tmp/alloy-span.json https://127.0.0.1/otlp/v1/traces
sleep 3
sudo grep -F "${TRACE_ID}" /var/lib/alloy/otlp/telemetry.json | grep -oE '"(stringValue":"checkout|name":"GET /cart)"' | sort -u

nginx accepts the span with the login, Alloy batches it and the file exporter writes it out:

HTTP 200
"name":"GET /cart"
"stringValue":"checkout"

Without the login, or with the UI login, the same request is refused before it reaches Alloy:

curl -sk -o /dev/null -w '%{http_code}\n' -X POST -H 'Content-Type: application/json' \
  --data-binary @/tmp/alloy-span.json https://127.0.0.1/otlp/v1/traces
curl -sk -o /dev/null -w '%{http_code}\n' https://127.0.0.1/otlp/v1/traces
401
405

(The second request is a GET: the ingest accepts only POST.)

In the UI, View on a component opens its page: its health, the arguments it runs with and what it exports. The OTLP receiver listens on the loopback address only:

The Alloy UI component page for otelcol.receiver.otlp.cloudimg, healthy, with its gRPC endpoint 127.0.0.1:4317

Open otelcol.processor.batch.cloudimg and choose Live debugging, then send the test trace again: each span appears in the page as it passes through the processor.

Live debugging of the batch processor showing GET /cart spans from the checkout service

Step 6: Connect your applications

Applications and collectors send OTLP/HTTP to https://<public-dns-name>/otlp (or the private IP address or your own DNS name, which the certificate names) with the otlp login. Copy the instance's certificate, /etc/ssl/alloy/alloy.crt, to the senders so they can verify it, or install a certificate your systems already trust (Step 10).

OpenTelemetry SDKs (Java, Python, Go, Node.js, .NET and others) read the standard environment variables. The header value is OTLP_AUTHORIZATION_HEADER from the credentials file with its space written as %20:

OTEL_SERVICE_NAME=my-service
OTEL_EXPORTER_OTLP_ENDPOINT=https://<public-dns-name>/otlp
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_EXPORTER_OTLP_HEADERS=Authorization=Basic%20<base64-of-otlp:password>
OTEL_EXPORTER_OTLP_CERTIFICATE=/etc/ssl/certs/alloy.crt

OpenTelemetry Collector or another Alloy. Use the otlphttp exporter with the otlp login:

extensions:
  basicauth/alloy:
    client_auth:
      username: otlp
      password: ${env:ALLOY_OTLP_PASSWORD}
exporters:
  otlphttp/alloy:
    endpoint: https://<public-dns-name>/otlp
    auth:
      authenticator: basicauth/alloy
    tls:
      ca_file: /etc/otelcol/alloy.crt
service:
  extensions: [basicauth/alloy]
  pipelines:
    traces:
      receivers: [otlp]
      exporters: [otlphttp/alloy]

The ingest refuses a request that carries browser signals (an Origin, Referer, Sec-Fetch-* header or a cookie) with HTTP 403: it is for machine clients only, so a web page cannot post telemetry on a visitor's behalf. Failed logins from one address are rate limited (HTTP 429 after a burst). Keep the password in a secrets store or a file only the sender can read, never in source code or an image you publish.

Step 7: Send telemetry on to your backend

The shipped pipeline in /etc/alloy/config.alloy receives OTLP, batches it and writes it to /var/lib/alloy/otlp/telemetry.json (100 MB per file, at most six files). To send telemetry on, replace the otelcol.exporter.file block with an exporter for your backend and point the batch processor's outputs at it. For example, traces to Grafana Tempo (or any OTLP endpoint) and metrics to a Prometheus-compatible remote write endpoint such as Mimir:

otelcol.processor.batch "cloudimg" {
  timeout = "1s"

  output {
    metrics = [otelcol.exporter.prometheus.backend.input]
    traces  = [otelcol.exporter.otlp.tempo.input]
  }
}

otelcol.exporter.otlp "tempo" {
  client {
    endpoint = "tempo.example.internal:4317"
  }
}

otelcol.exporter.prometheus "backend" {
  forward_to = [prometheus.remote_write.mimir.receiver]
}

prometheus.remote_write "mimir" {
  endpoint {
    url = "https://mimir.example.internal/api/v1/push"
  }
}

Edit the file as root (it stays root:alloy, mode 0640, which the pre-start check requires), then check it with alloy validate (with the same stability level the service runs at) and reload Alloy:

sudo -u alloy /usr/local/bin/alloy validate --stability.level=public-preview /etc/alloy/config.alloy && echo CONFIG_OK
sudo systemctl reload alloy
sleep 2
sudo curl -s http://127.0.0.1:12345/-/ready
CONFIG_OK
Alloy is ready.

If the reload is refused, Alloy keeps running the previous configuration and logs why (journalctl -u alloy -n 20); the UI shows a component that fails as unhealthy with its error. Backends on a private address need an egress allowance first (Step 9). The Alloy components reference lists every exporter, processor and receiver, including loki.write for logs and the Prometheus scrape components.

Step 8: Accept OTLP/gRPC on port 443 (optional)

OTLP over HTTP on port 443 works with every OpenTelemetry SDK and collector. If your senders must use OTLP over gRPC, let nginx forward the three OTLP gRPC services to Alloy's gRPC receiver behind the same otlp login. This adds one location to the ingest configuration both HTTPS servers include:

sudo tee -a /etc/nginx/alloy-ingest.conf >/dev/null <<'EOF'
# OTLP/gRPC ingest (traces, metrics, logs) with the 'otlp' login, added from the user guide (Step 8)
location ~ ^/opentelemetry\.proto\.collector\.(trace|metrics|logs)\.v1\.(Trace|Metrics|Logs)Service/Export$ {
    if ($alloy_ingest_browser) { return 403; }
    auth_basic "Grafana Alloy OTLP ingest";
    auth_basic_user_file /etc/nginx/alloy-otlp.htpasswd;
    client_max_body_size 20m;
    grpc_set_header Authorization "";
    grpc_pass grpc://127.0.0.1:4317;
}
EOF
sudo nginx -t 2>&1 | tail -n 1 && sudo systemctl reload nginx
nginx: configuration file /etc/nginx/nginx.conf test is successful

Check it with an empty export request (the gRPC framing of an empty message): with the login nginx forwards it to Alloy, which answers 200 with grpc-status: 0; without it nginx answers 401 (gRPC clients report UNAUTHENTICATED):

printf '\0\0\0\0\0' > /tmp/alloy-empty.grpc
curl -sk --http2 -D - -o /dev/null -X POST -H 'Content-Type: application/grpc' -H 'TE: trailers' \
  -H @<(sudo sed -n 's/^OTLP_AUTHORIZATION_HEADER=/Authorization: /p' /var/lib/cloudimg/alloy-credentials.txt) \
  --data-binary @/tmp/alloy-empty.grpc \
  https://127.0.0.1/opentelemetry.proto.collector.trace.v1.TraceService/Export | grep -iE '^(HTTP/2|grpc-status)' | tr -d '\r'
curl -sk --http2 -o /dev/null -w '%{http_code}\n' -X POST -H 'Content-Type: application/grpc' \
  --data-binary @/tmp/alloy-empty.grpc https://127.0.0.1/opentelemetry.proto.collector.trace.v1.TraceService/Export
HTTP/2 200
grpc-status: 0
401

Senders then use <public-dns-name>:443 as a gRPC endpoint with TLS and basic auth: in the OpenTelemetry Collector the otlp exporter with the same basicauth/alloy authenticator as in Step 6; in SDKs OTEL_EXPORTER_OTLP_PROTOCOL=grpc with the endpoint https://<public-dns-name>:443 and the same Authorization header. To close it again, delete the block from /etc/nginx/alloy-ingest.conf and reload nginx.

Step 9: Let Alloy reach a private address

The alloy account cannot open connections to the instance metadata service, link-local addresses or any private network, including your VPC. Public endpoints (for example Grafana Cloud) need no change. If Alloy sends to a backend or scrapes a target on a private address, allow that range first, one CIDR per line:

echo '10.0.12.0/24' | sudo tee -a /etc/cloudimg/alloy-egress-allow
sudo systemctl reload cloudimg-alloy-egress

The file accepts only private ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 100.64.0.0/10 and fc00::/7); metadata and link-local addresses can never be allowed.

Step 10: Your own DNS name and certificate

Point a DNS record at the instance (an Elastic IP keeps the address stable), then set the name Alloy is served as. This mints a new self-signed certificate for the name, updates nginx, the cross-site and cross-origin guards and the credentials file, and keeps the name across reboots:

sudo alloy-set-hostname <your-domain>

Give senders the new /etc/ssl/alloy/alloy.crt (or update their endpoint if they used the old name). To use a certificate from a public or internal certificate authority instead, copy it over the generated pair and reload nginx (running alloy-set-hostname again replaces it with a self-signed one):

sudo install -m 0644 <your-domain>.crt /etc/ssl/alloy/alloy.crt
sudo install -m 0600 <your-domain>.key /etc/ssl/alloy/alloy.key
sudo nginx -t && sudo systemctl reload nginx

Step 11: Change the passwords

sudo alloy-ui-password

This generates a new 24-character password for the UI login admin, keeps only its bcrypt hash in nginx's login file, writes the password to the credentials file and reloads nginx. Your browser asks for the new password on the next request. To issue a new ingest password (for example when someone who knew it leaves), run sudo alloy-otlp-password: every sender still using the old password is refused from then on, so update OTLP_AUTHORIZATION_HEADER (or the password) on each one. Read the new values from the credentials file (Step 4).

Step 12: Security checks

The shipped self-test signs in to the UI through nginx (and proves no login, a wrong password and the ingest login are refused), checks that the ingest refuses no login, a wrong password, the UI login, a GET and a browser request, sends a test span with the ingest login and finds it in the file exporter's output, and checks the cross-site, cross-origin and unknown-host guards, that Alloy listens on the loopback address only and that the instance metadata service cannot be reached from inside Alloy or nginx. Passwords reach curl only through root-only temporary files, never a command line:

sudo alloy-selftest
SELFTEST-OK https 301 + healthz + Alloy UI 401 none/wrong/ingest login, 200 per-instance admin + ready + otlp->batch->file pipeline healthy + OTLP ingest 401 none/wrong/UI login, 405 GET, 403 browser, 200 ingest login + span <id> in the file exporter + 403 cross-site /api and /-/reload + 403 cross-origin POST + 444 unknown host + loopback-only alloy (12345/4317/4318) + metadata 000 inside alloy and nginx (control 200)

The metadata block is a systemd setting on each service:

systemctl show -p IPAddressDeny alloy nginx
IPAddressDeny=fd00:ec2::254/128 168.63.129.16/32 169.254.169.254/32

IPAddressDeny=fd00:ec2::254/128 168.63.129.16/32 169.254.169.254/32

Usage reporting is off, and Alloy's UI and API, support bundle and profiling endpoints are fixed by its start command:

systemctl show -p ExecStart alloy | grep -oE -- '--(disable-reporting|server\.http\.[a-z.-]+(=[^ ;]+)?)'
--server.http.listen-addr=127.0.0.1:12345
--disable-reporting
--server.http.disable-support-bundle
--server.http.enable-pprof=false

A request for a host name the instance does not serve gets no answer at all (nginx closes the connection, so curl exits with code 52):

curl -sk --http1.1 -o /dev/null -w '%{http_code}\n' -H 'Host: attacker.example' https://127.0.0.1/healthz; echo "curl exit $?"
000
curl exit 52

The certificate was generated on this instance for its own names and addresses:

sudo openssl x509 -in /etc/ssl/alloy/alloy.crt -noout -subject -ext subjectAltName
subject=CN = <public-dns-name>, O = cloudimg
X509v3 Subject Alternative Name: 
    DNS:<public-dns-name>, IP Address:<public-ip>, IP Address:<private-ip>, DNS:<private-dns-name>, IP Address:127.0.0.1, DNS:localhost

Step 13: Versions, build record and files

/usr/local/bin/alloy --version | head -n 1
nginx -v 2>&1
grep -E '^(Grafana Alloy|build) ' /usr/share/doc/cloudimg-alloy/BUILD-INFO.txt | cut -c1-70
alloy, version v1.21.3 (branch: HEAD, revision: f2108cd)
nginx version: nginx/1.24.0 (Ubuntu)
Grafana Alloy     1.21.3 (commit f2108cd783accbd491b24843484c33abba6da
build             Go 1.27.3 (go1.27.3.linux-amd64.tar.gz sha256 65efbc
Path What it is
/usr/local/bin/alloy Grafana Alloy, run by alloy.service as the alloy user
/etc/alloy/config.alloy The pipeline (reload with sudo systemctl reload alloy)
/var/lib/alloy/data Alloy's storage (write-ahead logs and component state)
/var/lib/alloy/otlp/telemetry.json The shipped pipeline's output file (Step 7)
/etc/nginx/sites-available/cloudimg-alloy, /etc/nginx/alloy-ingest.conf nginx: HTTPS, the UI proxy with the admin login and the OTLP ingest with the otlp login
/etc/nginx/alloy-ui.htpasswd, /etc/nginx/alloy-otlp.htpasswd The bcrypt hashes of the two logins
/etc/ssl/alloy/ This instance's certificate and private key
/var/lib/cloudimg/alloy-credentials.txt URL, endpoints and both passwords (root only)
/etc/cloudimg/alloy-egress-allow Private ranges Alloy may reach (Step 9)
/usr/share/doc/cloudimg-alloy/ The licences, the build record with every checksum, the shipped configuration and the source offer

Step 14: Security recommendations

  • Keep port 443 open only to the people who use the UI and the networks your applications send from, and port 22 to your admin network. Never open 12345, 4317 or 4318.
  • Use your own DNS name and a certificate from a certificate authority your senders trust (Step 10).
  • Keep both passwords in a secrets store and change them (Step 11) when people leave. Telemetry can carry URLs, user identifiers and SQL statements, so treat it as sensitive data and use Alloy's processors (otelcol.processor.attributes, otelcol.processor.transform) to drop or mask what you do not need before it leaves the instance.
  • The instance applies Ubuntu security updates automatically. To update Alloy, read the release notes, back up /etc/alloy/config.alloy, and launch a newer version from the listing and point your senders at it.

Support and licensing

Grafana Alloy is open source software from Grafana Labs, licensed under the Apache License 2.0; this image ships Alloy built unmodified from the source of the v1.21.3 release tag, verified against a pinned SHA-256 checksum. Alloy's binary includes Grafana Loki packages licensed under the GNU Affero General Public License version 3; their licence, the licences of the other Go modules and a written source offer are in /usr/share/doc/cloudimg-alloy. nginx is licensed under the 2-clause BSD licence. cloudimg provides 24/7 support for this image by email at support@cloudimg.co.uk and through the cloudimg website: we help with launching, connecting applications and collectors, writing Alloy pipelines, sending to Tempo, Mimir, Loki, Prometheus or OTLP backends, TLS with your own domain, upgrades and troubleshooting.

Grafana, Grafana Alloy and the Grafana logo are trademarks or registered trademarks of Grafana Labs. OpenTelemetry is a trademark of The Linux Foundation. All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.