Storage Azure

Dragonfly P2P Distribution on Ubuntu 24.04 on Azure User Guide

| Product: Dragonfly Peer to Peer File and Image Distribution on Ubuntu 24.04 on Azure

Overview

This guide covers the deployment and use of Dragonfly on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images.

Dragonfly is an open source, cloud native peer to peer (P2P) file and container image distribution system, and a graduated project of the Cloud Native Computing Foundation. It turns the machines that pull artifacts into a swarm that shares pieces with each other, so a large image, model file or dataset is fetched from the origin once and then spread P2P between peers. This cuts origin bandwidth, speeds up mass rollouts, and stops registries and object stores from being overwhelmed when thousands of hosts pull the same content at once.

This image is a complete single node cluster. Dragonfly normally scales out across many hosts; this appliance runs the smallest useful shape of the same software on one VM, managed by systemd:

  • manager — the control plane and web console. It holds cluster configuration and users, and serves the REST and gRPC APIs. It is backed by a bundled MariaDB database and a Redis cache.
  • scheduler — builds the P2P scheduling graph, deciding which peer fetches which piece from whom.
  • dfdaemon (seed peer) — the peer agent running in seed peer mode. It fetches from the origin, seeds the swarm, and exposes an HTTP proxy and a dfget command line for pulling content through the P2P network.

Security by design. Dragonfly's manager ships a default console login of root / dragonfly. An appliance that shipped that unchanged would be wide open. This image inverts that:

  • The published default login is never present on your VM. At first boot the manager console is brought up on the loopback interface only, its root password is rotated to a value unique to your VM, and only then is the console rebound to the network. There is no window in which the published default is reachable from off the box, and the image itself contains no console password.
  • Nothing is baked into the image. The captured image contains no console password, no database password, no Redis password and no API signing key. All of them are generated uniquely on your VM at first boot and written to a root only file.
  • The database and cache are private. MariaDB and Redis listen on the loopback interface only and are never exposed to the network.
  • Proven before it ships. Every image is verified by pulling a real file end to end through the seed peer and scheduler and checksum matching the delivered bytes before it is published.

What is included:

  • Dragonfly server 2.5.1 (the manager and scheduler Go binaries) from the pinned upstream release, under systemd as dragonfly-manager.service and dragonfly-scheduler.service
  • Dragonfly client 1.4.9 (the Rust dfdaemon and dfget binaries) from the pinned upstream release, with dfdaemon running in seed peer mode as dragonfly-dfdaemon.service
  • MariaDB and Redis from the Ubuntu archive, preconfigured as the manager's backing store and tuned to run comfortably on a small VM
  • A first boot service that rotates every secret and registers the cluster before anything accepts connections

Prerequisites

  • An Azure subscription and the Azure CLI (az) installed and logged in (az login), or access to the Azure Portal.
  • An SSH key pair for Linux VM access.
  • A network security group that allows inbound TCP 22 (SSH) and 8080 (the manager web console) from your address, and, if you want other machines to pull through this node's proxy, 4001. Keep the manager console restricted to trusted addresses (see Security recommendations).

Deploy the virtual machine

Create a resource group and launch the image. Replace the image reference with the cloudimg Dragonfly offer from the Azure Marketplace.

az group create --name my-resource-group --location eastus

az vm create \
  --resource-group my-resource-group \
  --name dragonfly \
  --image <cloudimg-dragonfly-marketplace-image> \
  --size Standard_B2s \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard

# Open the console port to your address (and, optionally, the proxy port 4001):
az vm open-port --resource-group my-resource-group --name dragonfly --port 8080 --priority 1001

Note the public IP address that az vm create prints; this guide refers to it as <vm-ip>.

Confirm the services are healthy

SSH in:

ssh azureuser@<vm-ip>

Then check that all five services are active. The manager, scheduler and dfdaemon are exposed on the network; MariaDB and Redis are bound to the loopback interface only.

sudo systemctl is-active dragonfly-manager dragonfly-scheduler dragonfly-dfdaemon mariadb redis-server

All five report active, and the listening sockets show the manager on 8080 and 65003, the scheduler on 8002, the dfdaemon proxy and health ports on 4001 and 4003, and MariaDB and Redis on loopback only.

Terminal showing the Dragonfly manager, scheduler, dfdaemon, MariaDB and Redis services all active, with the manager, scheduler and dfdaemon ports listening and the database and cache bound to loopback.

The manager exposes an unauthenticated liveness probe. It returns HTTP 200 and nothing sensitive, so an Azure Load Balancer or Application Gateway health probe can use it directly.

curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/healthy

Retrieve your per VM credentials

The unique administrator password and every backing secret are written to a root only file at first boot. Read it over your SSH session:

sudo cat /root/dragonfly-oss-credentials.txt

You will see the manager console URL, the root administrator password unique to this VM, the proxy URL, and the database and Redis passwords. The values below are masked; yours are real and unique to your VM.

Terminal showing the per VM Dragonfly credentials file, readable only by root, listing the manager console URL, the unique root administrator password, the proxy URL and the database and Redis passwords, with the published default login proven to be rejected.

The health check answers 200, the per VM root password authenticates against the console API, and the published default root / dragonfly is rejected with HTTP 401:

Terminal showing the manager health endpoint returning HTTP 200, the per VM administrator password authenticating with HTTP 200, and the published default password rejected with HTTP 401.

Sign in to the Manager console

Open the manager web console in your browser at http://<vm-ip>:8080/. Sign in with the account root and the MANAGER_ADMIN_PASSWORD from your credentials file.

The Dragonfly Manager console sign in page, branded Dragonfly Console, with Account and Password fields.

After signing in, the Cluster overview shows your live control plane: one cluster (the default), one active scheduler, and the cluster-1 default cluster card. This is the managed P2P fabric this appliance registered at first boot.

The Dragonfly Manager Cluster overview after signing in, showing one cluster and one active scheduler, with the default cluster-1 card, logged in as root.

Open cluster-1 to see its detail: it is the default cluster, scheduler cluster ID 1, with its scopes and load limits. The Schedulers and Peers tabs list the fabric members.

The Dragonfly Manager cluster-1 detail page, showing it set as the default cluster with scheduler cluster ID 1, its scopes, and configuration such as seed peer and peer load limits.

Distribute a file through the P2P swarm

The point of Dragonfly is to move bytes peer to peer. dfget hands a download to the local dfdaemon, which asks the scheduler and pulls the content through the seed peer, fetching from the origin in pieces and caching them in the swarm.

The self test below serves a 6 MiB file from a local origin that speaks HTTP range requests, pulls it with dfget, and confirms the delivered bytes match the source checksum. It runs entirely on the VM and needs no internet access:

OD=$(mktemp -d); head -c 6291456 /dev/urandom > "$OD/model.bin"
SRC=$(sha256sum "$OD/model.bin" | awk '{print $1}')
cat > "$OD/range.py" <<'PY'
import http.server,os,sys,re
F=sys.argv[2]; SZ=os.path.getsize(F)
class H(http.server.BaseHTTPRequestHandler):
    def log_message(self,*a): pass
    def do_GET(self):
        d=open(F,'rb').read(); r=self.headers.get('Range')
        if r:
            m=re.match(r'bytes=(\d+)-(\d*)',r); s=int(m.group(1)); e=int(m.group(2)) if m.group(2) else SZ-1
            self.send_response(206); self.send_header('Content-Range',f'bytes {s}-{e}/{SZ}')
            self.send_header('Content-Length',str(e-s+1)); self.end_headers(); self.wfile.write(d[s:e+1])
        else:
            self.send_response(200); self.send_header('Content-Length',str(SZ)); self.send_header('Accept-Ranges','bytes'); self.end_headers(); self.wfile.write(d)
http.server.ThreadingHTTPServer(('127.0.0.1',18085),H).serve_forever()
PY
python3 "$OD/range.py" 18085 "$OD/model.bin" >/dev/null 2>&1 & SRV=$!
sleep 2
dfget http://127.0.0.1:18085/model.bin -O "$OD/pulled.bin"
kill "$SRV" 2>/dev/null
echo "source sha256: $SRC"
echo "dfget  sha256: $(sha256sum "$OD/pulled.bin" | awk '{print $1}')"
rm -rf "$OD"

The two checksums match: the file was pulled through the Dragonfly seed peer and scheduler, not fetched directly.

Terminal showing dfget pulling a 6 MiB file through the Dragonfly P2P path, with the source and dfget SHA256 checksums matching and a result line confirming the file transited the seed peer and scheduler.

In production you point Dragonfly at your real origins: for HTTP downloads route them through the dfdaemon proxy on port 4001 (curl -x http://<vm-ip>:4001 <url>), and for container images configure your registry mirror or containerd to use the proxy so image layers are distributed P2P.

Preheat content across the swarm

Under Job → Preheat the console can preheat content: pre distribute an image or file across the swarm before your hosts need it, so a mass rollout is served from peers instead of hammering the origin. On a fresh appliance the list is empty until you create your first preheat job.

The Dragonfly Manager Preheat page under the Job menu, showing the preheat workflow with an Add Preheat action and an empty task list on a fresh appliance.

Network ports

Port Service Exposure
22 SSH Network
8080 Manager REST API + web console Network (restrict to trusted addresses)
65003 Manager gRPC Network
8002 Scheduler gRPC Network
4001 dfdaemon HTTP/HTTPS proxy Network
4003 dfdaemon health Network
3306 MariaDB Loopback only
6379 Redis Loopback only

Managing the services

sudo systemctl status dragonfly-manager
sudo systemctl restart dragonfly-scheduler
sudo journalctl -u dragonfly-dfdaemon -f

The manager, scheduler and dfdaemon are enabled and restart on failure, and the whole stack survives a reboot. Configuration lives under /etc/dragonfly/ (manager.yaml, scheduler.yaml, dfdaemon.yaml).

Security recommendations

  • Restrict the manager console (port 8080). Limit it to trusted addresses with your network security group. Dragonfly's manager job API has a known advisory (CVE-2026-24124) in which preheat job endpoints are callable without authentication, so the console port should never be open to the whole internet.
  • Keep the per VM administrator password safe. It is unique to your VM and lives in /root/dragonfly-oss-credentials.txt (mode 0600). Rotate it in the console under your profile if needed.
  • The database and cache stay private. MariaDB and Redis are bound to loopback; do not expose them.
  • Keep the OS patched. Unattended security updates are enabled by default.

Support

These images are maintained by cloudimg with security updates and documentation. For deployment help or questions, contact support through the cloudimg listing. Dragonfly itself is an open source CNCF project; upstream documentation is at https://d7y.io and https://github.com/dragonflyoss.