Ag
Databases Azure

AgensGraph on Ubuntu 24.04 on Azure User Guide

| Product: AgensGraph on Ubuntu 24.04 on Azure

Overview

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

AgensGraph is a multi model graph database built on PostgreSQL. Version 2.17.0 tracks PostgreSQL 17.10, so everything you already know about PostgreSQL — ACID transactions, MVCC, roles and permissions, indexes, pg_dump, the whole driver ecosystem — applies unchanged. On top of that it adds a native property graph model and the openCypher query language, with partial ISO/GQL support.

The point of a multi model engine is that you do not have to choose. A graph traversal and a SQL join can run against the same database, in the same transaction, without an ETL hop between two systems. That makes it a natural fit for fraud rings, recommendation graphs, network and asset topologies, knowledge graphs and identity resolution, where the relationships matter as much as the rows.

The image is built from source at the pinned upstream release tag against Ubuntu 24.04's own OpenSSL 3, zlib, readline and ICU libraries. That matters for patching: the crypto AgensGraph links against is the distribution's, so ordinary unattended-upgrades on your VM keeps it current.

Security by design, no baked credential. The agens superuser role is password less by construction — the database is initialised with peer authentication on the local socket and no password is ever set at build time, so the captured image has no default or blank password to discover. On first boot each VM generates a unique password for the agens role and a unique self signed TLS server certificate, writes them to the root only file /root/agensgraph-credentials.txt, then enables TLS. Remote clients are accepted only over TLS (hostssl with scram-sha-256). Until first boot mints the certificate, the database is not reachable off box at all.

What is included:

  • AgensGraph 2.17.0 (PostgreSQL 17.10 base), built from source and running under systemd as agensgraph.service

  • The agens interactive client and the full PostgreSQL tool set (pg_dump, pg_restore, pg_basebackup, createdb, pg_isready and the rest), all on the default PATH

  • A default database graphdb containing a small demo property graph so you can run a real Cypher traversal the moment the VM boots

  • A per VM superuser password and a per VM TLS certificate generated on first boot, written to a root only credentials file

  • No swap in the image; the server is tuned to fit a Standard_B2s in RAM

Deploying the VM

Launch the image from the Azure Marketplace as you would any other VM. The recommended size is Standard_B2s (2 vCPU / 4 GiB), which the shipped postgresql.conf is tuned for. Larger graphs benefit from more memory — raise shared_buffers and effective_cache_size in proportion if you move to a bigger SKU.

AgensGraph listens on TCP 5432. The Azure network security group is your first line of defence: open 5432 only to the application subnets or client addresses that genuinely need it, never to the whole internet. TLS and the per VM password are the second and third layers, but the NSG is the one that keeps unwanted traffic off the box entirely.

First boot

The first time the VM boots, agensgraph-firstboot.service runs once and then disables itself. It:

  1. waits for AgensGraph to accept local connections,
  2. mints a per VM self signed TLS server certificate,
  3. sets ssl = on and reloads the server,
  4. generates a unique password for the agens superuser role,
  5. writes /root/agensgraph-credentials.txt (mode 600, owned by root),
  6. writes a message of the day pointer.

This normally completes within a few seconds of the database accepting connections. Nothing is required of you.

Retrieving your per VM credentials

The password is unique to your VM and is written in plain text only in this one root only file:

sudo cat /root/agensgraph-credentials.txt

Expected output (your host, and of course your password, will differ):

# AgensGraph — generated on first boot by agensgraph-firstboot.service.
# This password is UNIQUE to this VM. Store it somewhere safe; it is shown in
# plain text only here. Rotate with:
#   sudo -u agens agens -c "ALTER ROLE agens PASSWORD '<new>'"
# then update this file to match.

agensgraph.host=10.0.0.10
agensgraph.port=5432
agensgraph.role=agens
agensgraph.password=<unique to your VM>
agensgraph.database=graphdb
agensgraph.sslmode=require

Terminal showing the per VM credentials file at /root/agensgraph-credentials.txt with mode 600 owned by root, listing the AgensGraph host, port 5432, role agens, the per VM password, database graphdb and sslmode require, followed by a ready to use TLS connection string

Copy the password into your secret store now. To rotate it later, run sudo -u agens agens -c "ALTER ROLE agens PASSWORD '<your-new-password>'" and update the file to match.

Checking service health

sudo systemctl status agensgraph --no-pager

Expected output:

● agensgraph.service - AgensGraph 2.17.0 multi-model graph database
     Loaded: loaded (/etc/systemd/system/agensgraph.service; enabled; preset: enabled)
     Active: active (running) since Sun 2026-07-26 16:02:57 UTC
   Main PID: 40859 (postgres)
     CGroup: /system.slice/agensgraph.service
             ├─40859 /usr/local/agensgraph/bin/postgres -D /var/lib/agensgraph/data
             ├─40860 "postgres: logger "
             ├─40861 "postgres: checkpointer "

Confirm the server is accepting connections and report its version:

sudo -u agens pg_isready
/var/run/agensgraph:5432 - accepting connections
sudo -u agens agens -d graphdb -qtAc "SELECT version();"
PostgreSQL 17.10 (AgensGraph 2.17.0) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0, 64-bit

The version string names both layers: the PostgreSQL release that provides the storage and SQL engine, and the AgensGraph release that adds the graph model.

Terminal showing agensgraph.service active and running under systemd, pg_isready reporting the server accepting connections on port 5432, and the version query returning PostgreSQL 17.10 with AgensGraph 2.17.0

Connecting locally

agens is the AgensGraph interactive client — it is PostgreSQL's psql with Cypher support, and every psql flag works the same way. On the VM itself you connect through the local unix socket with peer authentication, so no password is needed:

sudo -u agens agens -d graphdb -qtAc "SELECT current_database(), current_user;"
graphdb|agens

For an interactive session, run sudo -u agens agens -d graphdb and type queries at the graphdb=# prompt. Use \q to quit and \? for the client's help.

Querying the demo graph

The graphdb database ships with a small property graph named demo: three :person vertices joined by two :knows edges. A graph is selected with SET graph_path, which behaves like PostgreSQL's search_path.

List the vertices and their properties:

sudo -u agens agens -d graphdb -c "SET graph_path = demo; MATCH (p:person) RETURN p.name AS name, p.city AS city ORDER BY p.name;"
SET
  name   |   city    
---------+-----------
 "alice" | "London"
 "bob"   | "Leeds"
 "carol" | "Bristol"
(3 rows)

Property values come back as JSON values, which is why they are quoted — a vertex property is a jsonb field, so it can hold a string, a number, an array or a nested object.

Now walk the relationships. This is the query shape a graph database exists for:

sudo -u agens agens -d graphdb -c "SET graph_path = demo; MATCH (a:person)-[r:knows]->(b:person) RETURN a.name AS from, b.name AS to, r.since AS since ORDER BY a.name;"
SET
  from   |   to    | since 
---------+---------+-------
 "alice" | "bob"   | 2019
 "bob"   | "carol" | 2021
(2 rows)

Edges carry properties too — here since records when each relationship was formed.

The real advantage shows up with a variable length traversal. [:knows*1..2] follows the knows relationship between one and two hops, so alice reaches bob directly and carol through bob, in a single query with no self join:

sudo -u agens agens -d graphdb -c "SET graph_path = demo; MATCH (a:person {name: 'alice'})-[:knows*1..2]->(b:person) RETURN b.name AS reachable;"
SET
 reachable 
-----------
 "bob"
 "carol"
(2 rows)

Widening that to *1..5 costs you two characters. The equivalent in plain SQL is a recursive common table expression that you would have to rewrite for every new depth and shape.

Terminal showing Cypher queries against the demo graph in AgensGraph: listing three person vertices with their name and city properties, walking the knows edges to return alice to bob since 2019 and bob to carol since 2021, then a variable length traversal from alice reaching both bob and carol

Connecting remotely over TLS

Remote clients are accepted only over TLS. Any libpq client works — agens, psql, JDBC, Python (psycopg), Node (pg), Go (pgx) — because the wire protocol is PostgreSQL's.

Using the values from your credentials file, with sslmode=require:

sudo bash -c 'PW=$(sed -n "s/^agensgraph.password=//p" /root/agensgraph-credentials.txt); agens "host=127.0.0.1 port=5432 dbname=graphdb user=agens password=$PW sslmode=require" -c "SELECT ssl, version AS tls_version, cipher FROM pg_stat_ssl WHERE pid = pg_backend_pid();"'
 ssl | tls_version |         cipher         
-----+-------------+------------------------
 t   | TLSv1.3     | TLS_AES_256_GCM_SHA384
(1 row)

ssl = t is the proof the session is genuinely encrypted, negotiated here as TLS 1.3. From your workstation, substitute your VM's public address:

agens "host=<your-vm-public-ip> port=5432 dbname=graphdb user=agens password=<your-per-vm-password> sslmode=require"

The certificate generated on first boot is self signed, so sslmode=require encrypts the connection without trying to verify the issuer. If you want clients to verify the server as well, replace /var/lib/agensgraph/data/server.crt and server.key with a certificate from your own CA, distribute the CA certificate to your clients, and have them connect with sslmode=verify-full.

Terminal showing a client connecting to AgensGraph with sslmode require and the pg_stat_ssl query returning ssl as t with TLS version 1.3 and the TLS_AES_256_GCM_SHA384 cipher, proving the session is encrypted end to end

Creating your own graph

Create a graph, add data and query it. These statements modify your database, so they are given inline rather than as a runnable block — run them in an interactive agens session or pass them with -c.

Create a graph and make it the default for the session: CREATE GRAPH social; then SET graph_path = social;

Create vertices with a label and properties: CREATE (:account {handle: 'ada', joined: 2021});

Create a relationship between two existing vertices: MATCH (a:account {handle: 'ada'}), (b:account {handle: 'grace'}) CREATE (a)-[:follows {weight: 1.0}]->(b);

Update a property on a matched vertex: MATCH (a:account {handle: 'ada'}) SET a.verified = true;

Delete a vertex and its relationships: MATCH (a:account {handle: 'ada'}) DETACH DELETE a;

Because AgensGraph is PostgreSQL underneath, all of this is transactional — wrap statements in BEGIN / COMMIT and a failed load rolls back cleanly.

For performance on larger graphs, index the properties you filter on with CREATE PROPERTY INDEX ON account (handle);. AgensGraph 2.17 also supports expression property indexes, including pgvector HNSW indexes over a ::vector(n) cast and GIN indexes over to_tsvector(...), which is what makes vector, full text and hybrid search over graph properties possible.

Mixing SQL and Cypher

Both languages address the same database. Ordinary SQL works exactly as it does in PostgreSQL:

sudo -u agens agens -d graphdb -c "SELECT schemaname, tablename FROM pg_tables WHERE schemaname = 'demo' ORDER BY tablename;"

Create ordinary relational tables with CREATE TABLE and query them with SELECT, alongside your graphs, in one transaction. Vertex and edge labels are stored as tables in a schema named after the graph, which is why the query above lists them — the graph is not a bolt on, it is native storage in the same engine.

Backup and restore

The standard PostgreSQL tooling covers graphs as well as tables, and is on the default PATH.

Dump the whole database, graphs included, and confirm the archive was written:

sudo -u agens pg_dump -d graphdb -Fc -f /tmp/graphdb.dump && sudo ls -lh /tmp/graphdb.dump
-rw-rw-r-- 1 agens agens 12K Jul 26 16:08 /tmp/graphdb.dump

Restore into a new database with sudo -u agens createdb graphdb_restored followed by sudo -u agens pg_restore -d graphdb_restored /tmp/graphdb.dump. Graphs survive the round trip intact — the restored database returns the same traversal results as the original.

For a physical backup of the whole cluster use pg_basebackup; AgensGraph 2.17 inherits PostgreSQL 17's incremental backup support, so pg_basebackup --incremental combined later with pg_combinebackup keeps routine backups small.

Move dumps off the VM promptly — a backup sitting on the same disk as the database is not a backup.

Where things live

Path Purpose
/usr/local/agensgraph Installation prefix (binaries in bin, libraries in lib)
/usr/local/bin agens and the PostgreSQL tools, on the default PATH
/var/lib/agensgraph/data Data directory, including postgresql.conf and pg_hba.conf
/var/log/agensgraph Server logs, rotated daily
/var/run/agensgraph Unix socket directory
/root/agensgraph-credentials.txt Per VM credentials, mode 600, root only
/etc/systemd/system/agensgraph.service systemd unit

Show the effective configuration at any time:

sudo -u agens agens -d graphdb -c "SELECT name, setting FROM pg_settings WHERE name IN ('listen_addresses','port','ssl','shared_buffers','work_mem','data_directory') ORDER BY name;"

After editing postgresql.conf, apply the change with sudo systemctl reload agensgraph for settings that support reload, or sudo systemctl restart agensgraph for those that need a restart. The pg_settings view's pending_restart column tells you which is which.

Tuning

The shipped configuration is sized for a Standard_B2s: shared_buffers 512MB, effective_cache_size 2GB, work_mem 8MB, maintenance_work_mem 128MB. The image deliberately ships with no swap. Swap on a cloud VM turns a memory shortage into unpredictable latency rather than a clean failure, so if your working set outgrows the VM the right answer is a larger SKU, not a swapfile.

Rough starting points when you scale up: set shared_buffers to about a quarter of RAM and effective_cache_size to about three quarters. Raise work_mem carefully — it is allocated per sort or hash node, not per connection, so a complex query can use several multiples of it at once.

Security checklist

  • Restrict the NSG. Allow 5432 only from the subnets or addresses that need it.
  • Rotate the per VM password into your secret store and treat /root/agensgraph-credentials.txt as the sensitive file it is.
  • Create a least privilege role for your application rather than connecting as the agens superuser. Create one with CREATE ROLE app LOGIN PASSWORD '<your-password>'; and grant only what it needs.
  • Tighten pg_hba.conf. The shipped file accepts TLS connections from 0.0.0.0/0; narrow that to your application subnet CIDR.
  • Replace the self signed certificate with one from your own CA if clients should verify the server, then move them to sslmode=verify-full.
  • Leave unattended-upgrades enabled so OpenSSL and the rest of the base system keep receiving security updates.

Troubleshooting

The service will not start. Check the unit and then the server's own log:

sudo systemctl status agensgraph --no-pager
sudo tail -n 40 /var/log/agensgraph/$(sudo ls -1 /var/log/agensgraph | tail -1)

No credentials file. It is written by agensgraph-firstboot.service on the first boot only. Check what happened:

sudo tail -n 30 /var/log/cloudimg-firstboot.log

A remote client cannot connect. Work outward: confirm the server is listening locally with sudo -u agens pg_isready, confirm TLS is on with sudo -u agens agens -d graphdb -qtAc "SHOW ssl;", then check the Azure NSG allows 5432 from your address. Remember that non TLS remote connections are refused by design — a client without sslmode=require will be rejected.

A Cypher query returns nothing unexpectedly. The most common cause is graph_path pointing at a different graph. Confirm which graph you are querying:

sudo -u agens agens -d graphdb -c "SET graph_path = demo; SHOW graph_path;"

Further reading

Support

This image is published by cloudimg. For help with the image itself, contact support@cloudimg.co.uk.