Application Infrastructure Azure

Dapr Distributed Application Runtime on Ubuntu 24.04 on Azure User Guide

| Product: Dapr 1.18.2 on Ubuntu 24.04 LTS on Azure

Overview

This guide covers the deployment and use of Dapr Distributed Application Runtime on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. Dapr (the Distributed Application Runtime) is a CNCF graduated project that gives microservices a set of portable, language agnostic building block APIs over HTTP and gRPC: state management, publish and subscribe messaging, reliable service invocation, bindings, secrets, actors and workflow. An application talks to a co located Dapr sidecar over loopback and gets these capabilities without hard coding any specific message broker, database or cloud service.

This image delivers a complete single node Dapr self hosted runtime, fully installed and configured, so you have a working Dapr environment answering within minutes of launch. It ships the Dapr runtime daprd 1.18.2 and the dapr CLI 1.18.1 (installed from the official upstream release binaries, verified by SHA256), the Dapr placement service for actors, and a local Redis instance pre wired as the default Dapr state store and pub/sub component. Every part runs as a hardened systemd service that comes back automatically after a reboot. A tiny bundled sample application and its Dapr sidecar are included and running, so a state save and get round trip and a publish and subscribe message flow work end to end out of the box.

Secure by default, no shared credentials. Nothing secret is baked into the image. On the first boot of every instance a unique Redis password is generated, written into the Redis configuration and rendered into the Dapr component definitions, and saved to a root only credentials file. The Dapr HTTP API (port 3500), gRPC API (port 50001), the placement service and Redis are all bound to the loopback interface only and are never exposed to the network. The only open port on the virtual machine is SSH, backed by a default deny firewall.

What is included:

  • Dapr runtime daprd 1.18.2 run under systemd (dapr-sidecar.service) as an unprivileged dapr user, serving the building block APIs for the bundled demo app on 127.0.0.1:3500 (HTTP) and 127.0.0.1:50001 (gRPC)

  • The Dapr placement service (dapr-placement.service) for actor placement, on loopback

  • A local Redis (redis-server.service) bound to 127.0.0.1:6379 with a per instance password, wired as the Dapr default state store and pub/sub component (/etc/dapr/components/)

  • A bundled sample subscriber app (dapr-sample-app.service) on 127.0.0.1:8088 that subscribes to the orders topic, so pub/sub is demonstrably working

  • The dapr CLI 1.18.1 at /usr/local/bin/dapr

  • A per instance Redis password, generated fresh on first boot, written to /root/dapr-credentials.txt

Prerequisites

  • Active Azure subscription, SSH public key, VNet + subnet in target region

  • Subscription to the Dapr listing on Azure Marketplace

  • Network Security Group rules allowing TCP 22 (admin) only — every Dapr and Redis endpoint is loopback and needs no inbound port

Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM). The Dapr runtime and placement service are compact Go binaries and a single node Redis fits comfortably. Increase the size for heavier workloads or a larger state store.

Step 1: Deploy from the Azure Portal

Search Dapr in Marketplace, select the cloudimg publisher, and click Create. Configure the Network Security Group to allow TCP 22 for administration. No other inbound port is required — your microservices run alongside the Dapr sidecar on the same VM and reach it over loopback.

Step 2: Deploy from the Azure CLI

RG="dapr-prod"; LOCATION="eastus"; VM_NAME="dapr1"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/dapr-ubuntu-24-04/versions/<version>"
SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"
az group create --name "$RG" --location "$LOCATION"
az vm create \
  --resource-group "$RG" --name "$VM_NAME" \
  --image "$GALLERY_IMAGE_ID" \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values "$SSH_KEY" \
  --public-ip-sku Standard
az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 22 --priority 1001

Step 3: Verify the runtime, placement and Redis services

SSH in as azureuser and confirm the four services are active and enabled (they survive reboot), and that the pinned Dapr CLI and runtime versions are installed.

for s in redis-server dapr-placement dapr-sample-app dapr-sidecar; do
  printf '%-22s %s / %s\n' "$s" "$(systemctl is-active $s)" "$(systemctl is-enabled $s)"
done
dapr version | grep -i 'CLI version'
/opt/dapr/bin/daprd --version | head -1

All four services report active / enabled, the CLI reports 1.18.1 and the runtime reports 1.18.2:

The four Dapr appliance services, redis-server, dapr-placement, dapr-sample-app and dapr-sidecar, all report active and enabled, the dapr CLI reports version 1.18.1 and the daprd runtime reports version 1.18.2

Step 4: Save and read state through the Dapr state API

The bundled Dapr sidecar exposes the state building block on 127.0.0.1:3500 for the app id demo. Save a key and read it back — the value round trips, and it is genuinely stored in Redis (Dapr prefixes keys with the app id, so the Redis key is demo||order1).

curl -s -X POST http://127.0.0.1:3500/v1.0/state/statestore \
  -H 'Content-Type: application/json' \
  -d '[{"key":"order1","value":"hello dapr"}]'
echo "saved (HTTP 204)"
curl -s http://127.0.0.1:3500/v1.0/state/statestore/order1; echo
REDIS_PASSWORD="$(sudo grep '^REDIS_PASSWORD=' /root/dapr-credentials.txt | cut -d= -f2-)"
redis-cli -h 127.0.0.1 -p 6379 -a "$REDIS_PASSWORD" --no-auth-warning EXISTS 'demo||order1'

The GET returns "hello dapr" and the Redis EXISTS check returns 1, proving the state is backed by Redis:

Saving a key through the Dapr state API returns HTTP 204, reading it back returns the value hello dapr, and a redis-cli EXISTS check on the demo prefixed key returns 1, proving the state is backed by the local Redis

Step 5: Publish a message through the Dapr pub/sub API

The bundled sample app subscribes to the orders topic on the pubsub component. Publish a message through the Dapr pub/sub building block and watch it arrive at the subscriber.

curl -s -X POST http://127.0.0.1:3500/v1.0/publish/pubsub/orders \
  -H 'Content-Type: application/json' \
  -d '{"orderId":"order-42"}'
echo "published (HTTP 204)"
sleep 2
sudo tail -n 2 /var/lib/dapr/received.log

Dapr delivers the message to the subscribed app as a CloudEvent, which the sample app records in /var/lib/dapr/received.log:

Publishing a message to the orders topic through the Dapr pub/sub API returns HTTP 204, and the subscribed sample app receives it, recording the delivered CloudEvent with the orderId in /var/lib/dapr/received.log

Step 6: Confirm the secure by default posture

Redis rejects unauthenticated access, Redis and the Dapr APIs listen on loopback only, the firewall is default deny except SSH, and the per instance credentials file is readable by root only.

redis-cli -h 127.0.0.1 -p 6379 PING || true
ss -ltn | grep -E ':6379|:3500|:50001|:50005' | awk '{print $1, $4}'
sudo ufw status | grep -E 'Status|22'
stat -c '%n %a %U:%G' /root/dapr-credentials.txt

The unauthenticated PING is rejected with NOAUTH, every Dapr and Redis socket is bound to 127.0.0.1, only SSH is allowed, and the credentials file is 600 root:root:

redis-cli PING without a password is rejected with NOAUTH, ss shows Redis and the Dapr HTTP, gRPC and placement sockets all bound to 127.0.0.1 only, ufw is enabled allowing only port 22, and the dapr-credentials.txt file is 600 root root

Step 7: Add your own components and wire in your services

Dapr components live in /etc/dapr/components/. The image ships statestore.yaml and pubsub.yaml, both backed by the local Redis. To point your own microservice at Dapr, run it alongside a daprd sidecar and call the same building block APIs on 127.0.0.1:3500.

ls -l /etc/dapr/components/
cat /root/dapr-credentials.txt | sed -n '1,12p'

To add a new component (for example a second state store, a binding, or a secret store), drop a Dapr component YAML into /etc/dapr/components/ and restart the sidecar with sudo systemctl restart dapr-sidecar. See the Dapr components reference for the full catalogue.

Security notes

  • No shared credential ships in the image. The Redis password is generated per instance on first boot and written to /root/dapr-credentials.txt (readable by root only). Redis rejects unauthenticated access.

  • Everything binds loopback. The Dapr HTTP API (3500), gRPC API (50001), placement (50005) and Redis (6379) listen on 127.0.0.1 only. The Dapr APIs are consumed by services running on the same VM, exactly as Dapr's sidecar model intends.

  • Default deny firewall. ufw allows only TCP 22 inbound. Add rules only for management access you actually need.

  • Keep the runtime current. cloudimg ships the image fully patched. Apply OS updates with sudo apt-get update && sudo apt-get upgrade, and consult cloudimg support for Dapr and Redis version upgrades.

Support

This image is backed by 24/7 cloudimg support with a one hour average response time for critical issues. Contact support@cloudimg.co.uk for help with Dapr component design, wiring the sidecar into your services and CI, state store and pub/sub configuration, and planning the move from this single node runtime to a production Kubernetes deployment.