Streaming & Messaging AWS

Arroyo on AWS User Guide

| Product: Arroyo on AWS

Overview

Arroyo is a distributed stream processing engine that lets you run SQL over real-time data streams. Instead of writing and operating a bespoke streaming application, you express your logic as a SQL query and Arroyo turns it into a stateful, fault-tolerant pipeline that processes events continuously as they arrive. It supports event-time semantics with watermarks, tumbling, hopping and sliding windows, streaming joins, stateful aggregation, user-defined functions, and exactly-once processing backed by periodic checkpointing, so results stay correct across restarts and late-arriving data.

This image runs Arroyo as a self-contained single-node cluster. The engine is a single binary at /usr/local/bin/arroyo, started by systemd as arroyo --config /etc/arroyo/config.toml cluster, which runs all of Arroyo's services in one process: the API service (REST plus the embedded web console), the admin, controller, compiler and node services. The metadata store is an embedded SQLite database, so there is no external database to provision, and pipeline state (the metadata database, checkpoints and compiler artifacts) lives under /var/lib/arroyo on the instance's own disk.

Arroyo ships with no authentication of its own, and its services bind to every network interface out of the box. This image hardens both: every Arroyo service is pinned to the loopback interface, and an nginx reverse proxy on port 80 is the only public listener. The console is protected by an nginx login prompt, and the engine's API additionally requires a bearer key that nginx presents on your behalf, so nothing reaches the engine unauthenticated. There is no default password: the administrator credential and the engine key are generated uniquely on each instance's first boot and are never baked into the image.

What is included:

  • Arroyo 0.15.0, installed from the official prebuilt release binary (dual-licensed Apache-2.0 OR MIT).
  • An nginx reverse proxy on port 80, fronting the console with HTTP Basic authentication and injecting the engine bearer key upstream. Ready for your own TLS certificate.
  • A per-instance administrator password and a per-instance engine API key, both generated on first boot and written to a root-only file.
  • systemd units for the engine, nginx and a one-shot first-boot service, so the stack starts automatically and restarts on failure.

Prerequisites

  • An AWS account with permission to subscribe to Marketplace products and launch EC2 instances.
  • A key pair in your target region for SSH access.
  • The recommended instance type is m5.large (2 vCPU, 8 GiB). Arroyo runs SQL pipelines with its built-in engine and needs no compiler toolchain at runtime, so first boot is fast and fully offline.
  • Inbound access on port 22 (SSH) and port 80 (the console) in the instance's security group. Restrict both to trusted source ranges.

Step 1 - Launch from the AWS Marketplace

  1. Open the product listing and choose Continue to Subscribe, then Continue to Configuration.
  2. Select the software version and your region, then Continue to Launch.
  3. Choose an instance type (m5.large or larger), your VPC and subnet, your key pair, and a security group that allows ports 22 and 80 from your trusted ranges.
  4. Launch the instance and note its public IPv4 address.

Step 2 - Launch from the AWS CLI

You can also launch the AMI directly. Replace the AMI id, key name, security group and subnet with your own:

aws ec2 run-instances \
  --image-id ami-xxxxxxxxxxxxxxxxx \
  --instance-type m5.large \
  --key-name your-key \
  --security-group-ids sg-xxxxxxxx \
  --subnet-id subnet-xxxxxxxx \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=arroyo}]'

Connecting to your instance

Connect over SSH as the default login user for your operating system variant. The login user depends on the variant you launched:

OS variant SSH login user
Ubuntu 24.04 ubuntu

For example, on the Ubuntu variant:

ssh -i /path/to/your-key.pem ubuntu@<public-ip>

The message-of-the-day banner shown at login points you to the console URL and the credentials file.

Step 3 - Retrieve the console credentials

On first boot the instance generates a unique administrator password and a unique engine API key and writes them to /root/arroyo-info.txt, readable only by root. Confirm the file is present and correctly owned:

sudo test -f /root/arroyo-info.txt && echo "credentials file present (mode $(sudo stat -c %a /root/arroyo-info.txt), owner $(sudo stat -c %U /root/arroyo-info.txt))"

Read the console username, password and URL:

sudo grep -E '^ARROYO_ADMIN_USERNAME=|^ARROYO_URL=' /root/arroyo-info.txt

The file also contains ARROYO_API_KEY, the bearer key you only need if you call the REST API directly on the loopback interface rather than through the console and its login. Keep this file secret.

Step 4 - Sign in to the console

Browse to http://<instance-public-ip>/ and sign in at the nginx login prompt with the username admin and the password from the credentials file. The Arroyo console opens on the pipelines list.

The Arroyo console pipelines list

Step 5 - Write and run your first pipeline

Choose Create Pipeline to open the SQL editor. Arroyo pipelines are written in SQL: you define a source table with a connector, then a SELECT that transforms it. The editor has syntax highlighting, a Check action that validates your SQL and draws the pipeline graph, and a Preview action that runs the query and streams live output before you launch it.

The Arroyo console SQL editor

A minimal self-contained example uses Arroyo's built-in impulse source, which generates a monotonic counter, and transforms it:

CREATE TABLE events WITH (connector = 'impulse', event_rate = '10');

SELECT counter AS event_id, counter * 10 + 7 AS score FROM events;

Choose Preview to see the transformed records stream in live, then Launch to start it as a durable pipeline. Once running, the pipeline detail view shows its operator graph from source through transform to sink, along with the pipeline id, job id and state.

A running pipeline's operator graph and job detail

Open the Outputs tab and choose Start tailing to watch the live output of a running pipeline.

Live pipeline output in the Arroyo console

Step 6 - Connect an external source such as Kafka

The impulse source above is a built-in generator for getting started. To process your own data, connect an external source or sink from the Connections area, or declare it inline in your SQL. For example, to read JSON events from a Kafka topic:

CREATE TABLE orders (
  order_id TEXT,
  amount FLOAT
) WITH (
  connector = 'kafka',
  bootstrap_servers = 'your-broker:9092',
  topic = 'orders',
  type = 'source',
  format = 'json'
);

SELECT order_id, amount FROM orders WHERE amount > 100;

Arroyo has built-in sources and sinks for Kafka, Kinesis, Redpanda, Pulsar, MQTT, NATS, WebSockets, server-sent events, Delta Lake, Iceberg and filesystem or object storage. You provision and operate the broker or store yourself; the appliance connects to it. See the Arroyo documentation for the full connector reference.

Verifying the deployment

Confirm the engine and proxy are running:

systemctl is-active arroyo.service nginx.service

Check the loopback health endpoint that nginx serves against the engine's admin service:

curl -s -o /dev/null -w 'health endpoint: HTTP %{http_code}\n' http://127.0.0.1/healthz

Confirm the installed Arroyo version:

arroyo --version

Confirm the console requires authentication - an unauthenticated request is rejected, and the correct password is accepted:

sudo bash -c 'P=$(grep "^ARROYO_ADMIN_PASSWORD=" /root/arroyo-info.txt | cut -d= -f2-); [ -n "$P" ] && echo "password present, length ${#P}"; curl -s -o /dev/null -w "no credentials  -> HTTP %{http_code}\n" http://127.0.0.1/api/v1/pipelines; curl -s -o /dev/null -w "correct password -> HTTP %{http_code}\n" -u "admin:$P" http://127.0.0.1/api/v1/pipelines'

You should see the unauthenticated request return 401 and the authenticated request return 200.

Confirm every Arroyo service is bound only to loopback, so nginx on port 80 is the sole public listener:

sudo ss -tlnp | grep -E ':5114|:5115|:5116|:5117|:5118' | grep -vE '127\.0\.0\.1|\[::1\]' | grep -q . && echo "WARNING: a service is on a public address" || echo "all Arroyo services are loopback-only"

Security model

This image adds two independent layers on top of an engine that has no authentication of its own:

  • Layer 1 - the browser login. nginx enforces HTTP Basic authentication on port 80 against a per-instance bcrypt password file. This is what a person signs in against, and it is the only public surface. It is ready for you to terminate TLS in front of it.
  • Layer 2 - the engine key. The Arroyo API is configured to require a static bearer key. nginx injects that key on every proxied request, so even a local process or an SSH port-forward that reaches the engine on 127.0.0.1:5115 directly is rejected without it.

Both secrets are generated per instance on first boot and written only to /root/arroyo-info.txt. Change the console password at any time by replacing <new-password> below:

echo '<new-password>' | sudo htpasswd -iB /etc/nginx/arroyo.htpasswd admin
sudo systemctl reload nginx

Operating the service

Arroyo and nginx are managed by systemd. Check status and view recent logs; restart the engine with sudo systemctl restart arroyo.service when needed:

sudo systemctl status arroyo.service --no-pager
sudo journalctl -u arroyo.service -n 100 --no-pager

Pipeline state, checkpoints and the embedded metadata database live under /var/lib/arroyo:

sudo du -sh /var/lib/arroyo

Enabling user-defined functions

SQL pipelines are planned and executed by Arroyo's built-in engine and need no compiler. User-defined functions (UDFs), however, are compiled from Rust, which requires a toolchain that this image intentionally does not ship, so first boot stays fast and offline. To use UDFs, enable the compiler toolchain by setting install-rustc = true and install-clang = true under [compiler] in /etc/arroyo/config.toml and restarting the engine; the compiler service will then fetch the toolchain on demand. Size the instance accordingly, as toolchain installation and compilation need additional CPU, memory and disk.

Adding HTTPS

nginx terminates plain HTTP on port 80. For production, place the console behind TLS. Point a DNS name at the instance, then either obtain a certificate with certbot and the nginx plugin, or terminate TLS at an Application Load Balancer in front of the instance. Restrict the security group so only your load balancer or trusted ranges can reach port 80.

Backup and maintenance

  • Back up pipeline state by snapshotting the root EBS volume, or by archiving /var/lib/arroyo while the engine is stopped for a consistent copy.
  • Rotate the console password with htpasswd as shown above.
  • Apply OS updates with sudo apt-get update && sudo apt-get upgrade and reboot during a maintenance window.
  • Upgrade Arroyo by replacing /usr/local/bin/arroyo with a newer release binary and restarting arroyo.service; review the Arroyo release notes for state-compatibility guidance first.

Troubleshooting

  • The console returns 500 on the login page. The per-instance password file is missing. This is the fail-closed state before first boot completes; wait for arroyo-firstboot.service to finish, or check sudo systemctl status arroyo-firstboot.service.
  • The console returns 502. The engine is not ready. Check sudo systemctl status arroyo.service and sudo journalctl -u arroyo.service.
  • A pipeline will not start. Open the pipeline's Errors tab in the console, and check the engine logs. Validation errors in your SQL are reported by the Check action in the editor before launch.

Support

cloudimg provides 24/7 technical support for this product by email and live chat, covering deployment, connecting external sources and sinks, writing and operating SQL pipelines, engine sizing, checkpointing and recovery, enabling UDFs, TLS termination, and upgrades. Contact support@cloudimg.co.uk.

Arroyo is distributed under the Apache License 2.0 or the MIT license, at your option. 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.