AgensGraph on Ubuntu 24.04 on Azure User Guide
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
agensinteractive client and the full PostgreSQL tool set (pg_dump,pg_restore,pg_basebackup,createdb,pg_isreadyand the rest), all on the defaultPATH -
A default database
graphdbcontaining 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_B2sin 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:
- waits for AgensGraph to accept local connections,
- mints a per VM self signed TLS server certificate,
- sets
ssl = onand reloads the server, - generates a unique password for the
agenssuperuser role, - writes
/root/agensgraph-credentials.txt(mode 600, owned by root), - 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

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.

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.

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.

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.txtas the sensitive file it is. - Create a least privilege role for your application rather than connecting as the
agenssuperuser. Create one withCREATE ROLE app LOGIN PASSWORD '<your-password>';and grant only what it needs. - Tighten
pg_hba.conf. The shipped file accepts TLS connections from0.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-upgradesenabled 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
- AgensGraph documentation: https://tech.skaiworldwide.com/docs
- AgensGraph source and releases: https://github.com/skaiworldwide-oss/agensgraph
- openCypher language reference: https://opencypher.org
- PostgreSQL 17 documentation: https://www.postgresql.org/docs/17/
Support
This image is published by cloudimg. For help with the image itself, contact support@cloudimg.co.uk.