Dgraph on Ubuntu 24.04 on Azure User Guide
Overview
Dgraph is a distributed, open source graph database built for high performance and horizontal scale, written in Go. It stores data as a graph of nodes and typed relationships and answers deeply joined traversals, full text, geo and regular expression queries in milliseconds through its own DQL query language and a native GraphQL API, over clean HTTP and gRPC endpoints. It is a strong foundation for knowledge graphs, recommendations, fraud detection, identity graphs and retrieval augmented generation.
The cloudimg image installs the official prebuilt Dgraph binary at /opt/dgraph/bin/dgraph, running as two coordinated systemd services from that single binary: dgraph-zero (the cluster coordinator) and dgraph-alpha (the data node). Both are started with --bindall=false, so every Dgraph listener binds to loopback 127.0.0.1 only. nginx terminates TLS on port 443 and reverse proxies the alpha HTTP API, so the credentials never cross the wire in plaintext.
Secure by default, no open database: open source Dgraph ships with no built in access control, an open API by default. This image never ships that. A dgraph-firstboot.service oneshot generates two independent secrets on each VM's first boot, both unique to the machine and never baked into the image:
- an nginx HTTP basic authentication username and password that gates every request to the HTTP API, and
- a Dgraph admin token that guards administrative operations such as altering the schema, exports and backups, restricted to the local host.
It also regenerates a per VM TLS certificate, resolves the instance URL and writes a root only info note, then disables itself. The dgraph-alpha start wrapper is fail closed: it refuses to launch without a token, so the database can never run open. No two VMs share credentials.
Dedicated data disk: all graph data (the zero write ahead log and the alpha postings, write ahead log and temp directories) lives on a dedicated data disk mounted at /var/lib/dgraph, captured into the image so every VM is provisioned with it. The binary and start wrappers stay on the OS disk.
What is included:
- Dgraph (verified at v25.3.8) as the official prebuilt Go binary, Apache 2.0 licensed
dgraph-zero.serviceanddgraph-alpha.servicerunning from the single binary, bound to loopback only- nginx terminating TLS on
443with HTTP basic authentication, reverse proxying the alpha HTTP API;80redirects to HTTPS dgraph-firstboot.servicefor the per VM basic auth credentials, admin token, TLS certificate and info note- A per VM admin token enforcing
X-Dgraph-AuthTokenon every administrative operation, plus a loopback only admin whitelist - A dedicated data disk mounted at
/var/lib/dgraph, and a swap file provisioned per VM - Ubuntu 24.04 LTS base, fully patched
- 24/7 cloudimg support, 24h response SLA
Prerequisites
An active Azure subscription, an SSH key pair, and a VNet with a subnet. Dgraph performance and capacity scale with memory. Recommended VM size: Standard_B2s (2 vCPU, 4 GB RAM) for evaluation and small graphs, or a Standard_D4s_v5 or larger for production workloads. The dedicated data disk holds your graph, so resize it to suit your dataset.
Step 1: Deploy from the Azure Portal
Search the Marketplace for Dgraph on Ubuntu 24.04, choose your VM size, and attach an NSG that allows TCP 22 (SSH) from your management network and TCP 443 (HTTPS API) from the networks and applications that need to query it. Port 80 only issues an HTTPS redirect. The Dgraph ports (5080, 6080, 7080, 8080, 9080) are bound to loopback and are never exposed.
Step 2: Deploy from the Azure CLI
RG="dgraph-prod"; LOCATION="eastus"; VM_NAME="dgraph-01"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/dgraph-ubuntu-24-04/versions/<version>"
SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"
az group create --name "$RG" --location "$LOCATION"
az network vnet create -g "$RG" --name dgraph-vnet --address-prefix 10.90.0.0/16 --subnet-name dgraph-subnet --subnet-prefix 10.90.1.0/24
az network nsg create -g "$RG" --name dgraph-nsg
az network nsg rule create -g "$RG" --nsg-name dgraph-nsg --name allow-ssh --priority 100 \
--source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 22 --access Allow --protocol Tcp
az network nsg rule create -g "$RG" --nsg-name dgraph-nsg --name allow-https --priority 110 \
--source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 443 --access Allow --protocol Tcp
az vm create -g "$RG" --name "$VM_NAME" --image "$GALLERY_IMAGE_ID" \
--size Standard_B2s --storage-sku StandardSSD_LRS \
--admin-username azureuser --ssh-key-values "$SSH_KEY" \
--vnet-name dgraph-vnet --subnet dgraph-subnet --nsg dgraph-nsg --public-ip-sku Standard
Step 3: Connect via SSH
ssh azureuser@<vm-ip>
Step 4: Verify the services are running
Dgraph zero and alpha bind to loopback only; nginx fronts the alpha HTTP API with TLS on port 443. The static /healthz probe is public; the database API requires your credentials.
for s in dgraph-zero.service dgraph-alpha.service nginx.service; do
printf '%-26s %s\n' "$s" "$(systemctl is-active $s)"
done
ss -tln | grep -E '127.0.0.1:(5080|6080|7080|8080|9080)|:443 '
curl -ks -o /dev/null -w 'GET /healthz -> HTTP %{http_code}\n' https://127.0.0.1/healthz
cat /opt/dgraph/VERSION
You should see all three services active, every Dgraph port bound to 127.0.0.1, the /healthz probe returning 200, and the installed Dgraph version.
dgraph-zero.service active
dgraph-alpha.service active
nginx.service active
LISTEN 0 4096 127.0.0.1:5080 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:6080 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:7080 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:8080 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:9080 0.0.0.0:*
LISTEN 0 511 0.0.0.0:443 0.0.0.0:*
GET /healthz -> HTTP 200
dgraph v25.3.8 (v25.3.8) - official prebuilt release, Apache-2.0

Step 5: Read your per VM credentials and confirm auth is enforced
The nginx basic auth credentials and the Dgraph admin token were generated on this VM's first boot and stored in a root only file. Read them and keep them secret. Then confirm that unauthenticated requests are rejected and that administrative operations require the token.
sudo cat /root/dgraph-info.txt
DUSER=$(sudo grep '^DGRAPH_HTTP_USER=' /root/dgraph-info.txt | cut -d= -f2-)
DPASS=$(sudo grep '^DGRAPH_HTTP_PASSWORD=' /root/dgraph-info.txt | cut -d= -f2-)
DTOKEN=$(sudo grep '^DGRAPH_SECURITY_TOKEN=' /root/dgraph-info.txt | cut -d= -f2-)
echo "no basic auth -> HTTP $(curl -ks -o /dev/null -w '%{http_code}' https://127.0.0.1/health)"
echo "with basic auth -> HTTP $(curl -ks -o /dev/null -w '%{http_code}' -u "$DUSER:$DPASS" https://127.0.0.1/health)"
echo "alter without token -> $(curl -ks -u "$DUSER:$DPASS" -X POST https://127.0.0.1/alter -d 'name: string .' | head -c 90)"
The info note holds the HTTP username and password, the admin token and the instance URLs; it is owned root:root with mode 0600. Unauthenticated calls return 401; a schema alter without the admin token is refused.
DGRAPH_HTTP_USER=dgraph_xxxxxx
DGRAPH_HTTP_PASSWORD=****************
DGRAPH_SECURITY_TOKEN=****************
DGRAPH_URL=https://10.0.0.4/
DGRAPH_HEALTH_URL=https://10.0.0.4/health
no basic auth -> HTTP 401
with basic auth -> HTTP 200
alter without token -> {"errors":[{"message":"No Auth Token found. Token needed for Admin operations."

Step 6: Set a schema, add data, and run your first query
Load your credentials into shell variables. Set a small schema with the admin token, add a couple of connected nodes with an RDF mutation, then run a DQL query that traverses the friend edge. Administrative operations such as /alter require the X-Dgraph-AuthToken header; data operations such as /mutate and /query need only your basic auth.
DUSER=$(sudo grep '^DGRAPH_HTTP_USER=' /root/dgraph-info.txt | cut -d= -f2-)
DPASS=$(sudo grep '^DGRAPH_HTTP_PASSWORD=' /root/dgraph-info.txt | cut -d= -f2-)
DTOKEN=$(sudo grep '^DGRAPH_SECURITY_TOKEN=' /root/dgraph-info.txt | cut -d= -f2-)
curl -ks -u "$DUSER:$DPASS" -H "X-Dgraph-AuthToken: $DTOKEN" -X POST https://127.0.0.1/alter \
-d 'name: string @index(exact, term) .
age: int .
friend: [uid] .'
curl -ks -u "$DUSER:$DPASS" -H 'Content-Type: application/rdf' \
-X POST "https://127.0.0.1/mutate?commitNow=true" -d '{ set {
_:alice <name> "Alice" .
_:alice <age> "32" .
_:bob <name> "Bob" .
_:bob <age> "29" .
_:alice <friend> _:bob .
} }' | jq -c '{code: .data.code}'
curl -ks -u "$DUSER:$DPASS" -H 'Content-Type: application/dql' -X POST https://127.0.0.1/query \
-d '{ people(func: has(friend)) { name age friend { name age } } }' | jq '.data'
The alter returns Success, the mutation commits the n quads, and the query traverses the graph and returns Alice with her friend Bob nested inside.
{"data":{"code":"Success","message":"Done"}}
{"code":"Success"}
{
"people": [
{
"name": "Alice",
"age": 32,
"friend": [
{ "name": "Bob", "age": 29 }
]
}
]
}

Step 7: Query from a remote client
From your workstation, point any HTTP client at https://<vm-ip>/. Because the certificate is self signed per VM, pass -k (or trust /etc/nginx/tls/dgraph.crt). Supply the basic auth username and password on every request, and add the admin token header for administrative operations. For example, a DQL query from your own machine:
curl -k -u "$DUSER:$DPASS" -H 'Content-Type: application/dql' \
-X POST https://<vm-ip>/query -d '{ people(func: has(name)) { name } }'
Dgraph also serves a native GraphQL API at /graphql and a GraphQL admin API at /admin, and a gRPC endpoint on port 9080 (kept on loopback in this image). Official client libraries are available for Go, Python, JavaScript, Java and more; point them at the HTTPS endpoint with your basic auth and token.
Step 8: Persistence, first boot and updates
All graph data lives on the dedicated data disk mounted at /var/lib/dgraph (the zero write ahead log and the alpha postings, write ahead log and temp directories) and is captured into the image, so a VM launched from this image is ready immediately. The first boot service generates the per VM credentials, admin token and TLS certificate, provisions a swap file and writes a root only info note, then disables itself so subsequent reboots are unaffected. The OS ships fully patched with unattended security upgrades enabled.
/opt/dgraph/bin/dgraph version | grep -i 'Dgraph version'
systemctl is-enabled dgraph-firstboot.service
findmnt -no SOURCE,TARGET,FSTYPE /var/lib/dgraph
apt-mark showhold
Dgraph version : v25.3.8
disabled
/dev/sdc /var/lib/dgraph ext4
The first boot oneshot shows disabled because it ran once on first boot and disabled itself, the graph data is on the mounted ext4 data disk, and apt-mark showhold is empty, confirming no security updates are held back.

Step 9: Enable a real TLS certificate
The appliance ships with a self signed certificate generated per VM. For production, point a DNS record at the VM and replace it with a CA signed certificate. With certbot you can obtain one and drop it into the nginx TLS paths.
sudo apt-get update && sudo apt-get install -y certbot
sudo certbot certonly --standalone -d your-domain.example.com
sudo cp /etc/letsencrypt/live/your-domain.example.com/fullchain.pem /etc/nginx/tls/dgraph.crt
sudo cp /etc/letsencrypt/live/your-domain.example.com/privkey.pem /etc/nginx/tls/dgraph.key
sudo systemctl reload nginx
Clients can then verify the certificate normally instead of using -k. To rotate the per VM credentials and admin token at any time, remove the sentinel and re run the first boot service:
sudo rm -f /var/lib/cloudimg/dgraph-firstboot.done
sudo systemctl start dgraph-firstboot.service
sudo systemctl restart dgraph-alpha nginx
Support
Every cloudimg deployment includes 24/7 support with a 24 hour response SLA. Contact support@cloudimg.co.uk for help with deployment, configuration or scaling.
This is a repackaged open source software product with additional charges for cloudimg support services. Dgraph is a trademark of its respective owner. 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.