Databases Azure

Neo4j 2026 Community on Ubuntu 24.04 on Azure User Guide

| Product: Neo4j 2026 Community on Ubuntu 24.04 LTS on Azure

Videos

Overview: Neo4j 2026 Community Ubuntu 24.04 on Azure: overview (watch on YouTube)
Launch and log in: Neo4j 2026 Community Ubuntu 24.04 on Azure: launch and log in (watch on YouTube)
Deep Dive: Neo4j 2026 Community Ubuntu 24.04 on Azure: deep dive (watch on YouTube)

Overview

This guide covers deploying and using Neo4j 2026 Community on Ubuntu 24.04 from cloudimg on Microsoft Azure.

Neo4j is a native graph database: data is stored as nodes, relationships and properties, and queried with the Cypher query language. It is used for knowledge graphs, fraud detection, recommendations, network and IT topology, identity and access graphs, and as the graph store behind retrieval-augmented generation (GraphRAG). This image ships Neo4j Community Edition 2026.10.1 with Neo4j Browser (the web query interface), the HTTP API and the Bolt protocol used by every official Neo4j driver, plus cypher-shell. Neo4j Community Edition is licensed under the GNU General Public License version 3.

What cloudimg adds to the upstream build:

  • No default login. Neo4j normally starts with the user neo4j and the password neo4j. On this image the database is created on first boot with a 24-character password that is unique to the VM, set before Neo4j ever starts, so the default login never exists. Nothing listens until that password and the certificate exist.
  • Encrypted from the first boot. A certificate generated on the VM for its own address protects Neo4j Browser and the HTTP API (HTTPS on port 7473) and the Bolt protocol (port 7687). Bolt requires TLS: plain bolt:// connections are refused. The unencrypted HTTP port 7474 is turned off, and every other Neo4j listener is off or bound to loopback. The only ports the VM listens on are 22, 7473 and 7687.
  • A sandboxed database service. Neo4j runs as an unprivileged service account in a systemd sandbox: it can write only its data, log and run directories, and it cannot read the credentials file, SSH keys, /root or /etc/shadow. LOAD CSV file URLs are confined to the import directory.
  • Blocked from the Azure metadata service. The Neo4j service (and its first-boot setup) cannot reach the Azure Instance Metadata Service (169.254.169.254) or the Azure wire server (168.63.129.16), so a LOAD CSV FROM 'http://...' cannot be used to read VM metadata. The Azure Linux Agent and cloud-init keep their access, so provisioning and extensions work as usual.
  • No plugins. The Neo4j package also carries the APOC, Graph Data Science and GenAI plugin jars. None is installed on this image, the plugins directory is empty, and upgrades do not restore them. Usage reporting to Neo4j is turned off.
  • Fully patched Ubuntu 24.04 at build time, Neo4j from Neo4j's official signed package repository, OpenJDK 25 from Ubuntu, and the cloudimg security defaults.

What is on the image: Ubuntu 24.04 LTS, OpenJDK 25 (Java runtime, from Ubuntu), Neo4j 2026.10.1 Community and cypher-shell from debian.neo4j.com, the configuration in /etc/neo4j/, the data directory /var/lib/neo4j/data (empty until first boot creates the databases), and the cloudimg first-boot service.

Connecting to your VM

Item Value
SSH user the administrator you choose at deployment (for example azureuser)
Neo4j Browser https://<vm-ip>:7473/browser/
Bolt (drivers, cypher-shell) neo4j+s://<vm-ip>:7687 (or neo4j+ssc:// with the self-signed certificate)
Neo4j user neo4j, password in /root/neo4j-credentials.txt
Open ports 22 (SSH), 7473 (HTTPS), 7687 (Bolt, TLS). Port 7474 is not used.

Prerequisites

  1. An Azure subscription, an SSH public key, and a virtual network in the target region
  2. A network security group that allows TCP 22 from your own address, and TCP 7473 (Neo4j Browser and the HTTP API, HTTPS) and TCP 7687 (Bolt, TLS) from the clients and applications that will use the database. Expose 7473 and 7687 only to the networks that need them; you can also keep them closed and use an SSH tunnel (Step 7).

Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM) for development and small graphs. Neo4j sizes its heap and page cache from the VM's memory at start-up; for production graphs choose a memory-optimised size such as Standard_E4s_v5 (4 vCPU, 32 GB RAM) or larger, and see Step 9 for tuning.

Step 1: Deploy from the Azure Portal

Search for Neo4j 2026 in the Azure Marketplace, select the cloudimg entry and choose Create. In Networking, allow SSH (22) from your address, and add inbound rules for TCP 7473 and TCP 7687 from your client networks. First boot takes one to two minutes after the VM is running, while it generates the VM's certificate and password, creates the databases and starts Neo4j.

Step 2: Deploy from the Azure CLI

RG="neo4j-prod"; LOCATION="eastus"; VM_NAME="neo4j-01"
IMAGE_URN="cloudimg1647283583153:neo4j-2026-ubuntu-24-04:default:latest"
CLIENT_CIDR="<your-mgmt-cidr>"
az group create --name "$RG" --location "$LOCATION"
az network nsg create -g "$RG" --name neo4j-nsg
az network nsg rule create -g "$RG" --nsg-name neo4j-nsg --name allow-ssh --priority 100 \
  --source-address-prefixes "$CLIENT_CIDR" --destination-port-ranges 22 --access Allow --protocol Tcp
az network nsg rule create -g "$RG" --nsg-name neo4j-nsg --name allow-neo4j --priority 110 \
  --source-address-prefixes "$CLIENT_CIDR" --destination-port-ranges 7473 7687 --access Allow --protocol Tcp
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn "$IMAGE_URN"
az vm create -g "$RG" --name "$VM_NAME" --image "$IMAGE_URN" \
  --size Standard_B2s --admin-username azureuser --ssh-key-values ~/.ssh/id_rsa.pub \
  --nsg neo4j-nsg --public-ip-sku Standard

First boot takes a minute or two. If you connect as soon as the VM shows as running, wait for the per-VM setup to finish before continuing:

bash sudo cloud-init status --wait

It prints status: done; systemctl is-active neo4j-firstboot.service then reports active.

Step 3: Connect via SSH and check the service

ssh azureuser@<vm-ip>

The login banner shows the Neo4j Browser and Bolt addresses for this VM. Confirm Neo4j is running and listening only on the HTTPS and Bolt ports:

systemctl is-active neo4j.service neo4j-firstboot.service
sudo ss -Htln '( sport = :7473 or sport = :7474 or sport = :7687 )'

Both services report active. Neo4j listens on port 7473 (HTTPS) and port 7687 (Bolt with TLS) on all addresses; nothing listens on 7474:

neo4j.service and neo4j-firstboot.service active, Neo4j listening only on 7473 and 7687, plain HTTP disabled and Bolt TLS required in neo4j.conf

Step 4: Read the per-VM credentials

The neo4j password is unique to this VM and is written to a root-only file (mode 0600) at first boot. Print only the field names, never the password itself:

sudo grep -oE '^[A-Z0-9_]+=' /root/neo4j-credentials.txt

This lists NEO4J_HOST (the address Neo4j advertises: the VM's public IP, or its private IP when it has none), NEO4J_USER (neo4j), NEO4J_PASSWORD (the per-VM password), NEO4J_BROWSER_URL, NEO4J_HTTPS_URL and NEO4J_BOLT_URI. Read the password with sudo sed -n 's/^NEO4J_PASSWORD=//p' /root/neo4j-credentials.txt when you need it, and keep it safe: it is the database administrator's password. The VM's certificate covers its public and private IP addresses, its host name, localhost and 127.0.0.1:

The 0600 root-only credentials file listed by field name, and the per-VM certificate with the VM's addresses as subject alternative names

Step 5: The authentication gate

Neo4j's HTTP API (the Query API at /db/neo4j/query/v2) refuses an anonymous request, a wrong password and the documented default neo4j/neo4j with 401 Unauthorized. Save the VM's certificate so curl can verify the connection:

sudo cat /var/lib/neo4j/certificates/cloudimg/public.crt > /tmp/neo4j.crt
H=$(sudo sed -n 's/^NEO4J_HOST=//p' /root/neo4j-credentials.txt)
Q='{"statement":"RETURN 1 AS one"}'
curl -s --cacert /tmp/neo4j.crt --resolve "$H:7473:127.0.0.1" -o /dev/null -w 'anonymous:      HTTP %{http_code}\n' -H 'Content-Type: application/json' -d "$Q" "https://$H:7473/db/neo4j/query/v2"
curl -s --cacert /tmp/neo4j.crt --resolve "$H:7473:127.0.0.1" -u 'neo4j:wrong-password' -o /dev/null -w 'wrong password: HTTP %{http_code}\n' -H 'Content-Type: application/json' -d "$Q" "https://$H:7473/db/neo4j/query/v2"
curl -s --cacert /tmp/neo4j.crt --resolve "$H:7473:127.0.0.1" -u 'neo4j:neo4j' -o /dev/null -w 'neo4j/neo4j:    HTTP %{http_code}\n' -H 'Content-Type: application/json' -d "$Q" "https://$H:7473/db/neo4j/query/v2"

Each returns HTTP 401. Neo4j locks an account for a few seconds after three failed logins in a row, so wait a moment before using the real password. With the per-VM password the query runs:

sleep 6
H=$(sudo sed -n 's/^NEO4J_HOST=//p' /root/neo4j-credentials.txt)
printf 'user = "neo4j:%s"\n' "$(sudo sed -n 's/^NEO4J_PASSWORD=//p' /root/neo4j-credentials.txt)" | \
  curl -s --cacert /tmp/neo4j.crt --resolve "$H:7473:127.0.0.1" -K - -w '\nHTTP %{http_code}\n' \
  -H 'Content-Type: application/json' -d '{"statement":"RETURN 1 AS one"}' "https://$H:7473/db/neo4j/query/v2"

The response is HTTP 202 with "data":{"fields":["one"],"values":[[1]]}. (The password is passed to curl on standard input, so it does not appear in the process list.)

Step 6: Use cypher-shell over TLS

cypher-shell reads the password from the NEO4J_PASSWORD environment variable (set it in each new shell, as below). Because Bolt requires TLS, use the bolt+ssc:// scheme on the VM itself (encrypted; the self-signed certificate is not checked):

export NEO4J_PASSWORD="$(sudo sed -n 's/^NEO4J_PASSWORD=//p' /root/neo4j-credentials.txt)"
echo "CALL dbms.components() YIELD name, versions, edition RETURN name, versions[0] AS version, edition;" | \
  cypher-shell -a bolt+ssc://localhost:7687 -u neo4j --format plain

It prints "Neo4j Kernel", "2026.10.1", "community". A plain, unencrypted connection is refused:

export NEO4J_PASSWORD="$(sudo sed -n 's/^NEO4J_PASSWORD=//p' /root/neo4j-credentials.txt)"
echo "RETURN 1;" | cypher-shell -a bolt://localhost:7687 -u neo4j --format plain 2>&1 | head -3 || true

Create a small graph and query it:

export NEO4J_PASSWORD="$(sudo sed -n 's/^NEO4J_PASSWORD=//p' /root/neo4j-credentials.txt)"
cat <<'EOF' | cypher-shell -a bolt+ssc://localhost:7687 -u neo4j --format plain
MERGE (alice:Person {name: 'Alice'})
MERGE (bob:Person {name: 'Bob'})
MERGE (carol:Person {name: 'Carol'})
MERGE (graphs:Topic {name: 'Graph databases'})
MERGE (alice)-[:KNOWS]->(bob)
MERGE (bob)-[:KNOWS]->(carol)
MERGE (alice)-[:INTERESTED_IN]->(graphs)
MERGE (carol)-[:INTERESTED_IN]->(graphs);
MATCH (p:Person)-[:KNOWS*1..2]->(friend:Person)
WHERE p.name = 'Alice'
RETURN friend.name AS reachable_from_alice ORDER BY reachable_from_alice;
EOF

The query returns Bob and Carol: Carol is reached through Bob, a two-hop traversal.

cypher-shell over bolt+ssc reporting Neo4j Kernel 2026.10.1 community, a plain bolt connection refused, and the two-hop traversal returning Bob and Carol

Step 7: Open Neo4j Browser

With TCP 7473 and 7687 open to your address in the network security group, open https://<vm-ip>:7473/browser/ in your browser. If you prefer to keep those ports closed, use an SSH tunnel instead and open https://localhost:7473/browser/ (the VM's certificate also covers localhost):

ssh -N -L 7473:localhost:7473 -L 7687:localhost:7687 azureuser@<vm-ip>

The certificate is self-signed and unique to the VM, so your browser warns once; accept it for this host (for production, see Step 9). In the Connect to instance dialog choose neo4j+s:// as the protocol, enter the VM's address and port 7687 as the connection URL (localhost:7687 through the tunnel), keep neo4j as the database user, and enter the per-VM password:

Neo4j Browser Connect to instance dialog with the neo4j+s protocol, a Bolt address on port 7687 and the neo4j user

If the connection fails with a security or certificate message, your browser has not yet accepted the certificate for the Bolt port: open https://<vm-ip>:7687 (or https://localhost:7687) once in a new tab, accept the warning (the page itself then shows an error, which is expected), and connect again.

Once connected, type Cypher in the editor at the top and choose Run. The graph from Step 6 is drawn as nodes and relationships, with a summary of the labels and relationship types:

Neo4j Browser showing the Person and Topic graph with KNOWS and INTERESTED_IN relationships

Queries that return values are shown as a table, here the two-hop traversal from Alice:

Neo4j Browser table result of a two-hop KNOWS traversal from Alice to Bob and Carol

:sysinfo lists the databases (neo4j and system), the address they are served on and their status:

Neo4j Browser sysinfo panel with the neo4j and system databases online at the VM's advertised address

Step 8: Connect from an application

Every Neo4j driver (Python, Java, JavaScript, .NET, Go) connects over Bolt on port 7687 with TLS. With the self-signed certificate use the neo4j+ssc:// scheme (encrypted, certificate not checked); to verify the server, copy /var/lib/neo4j/certificates/cloudimg/public.crt from the VM and add it to the client's trust store, or replace the certificate with one from your own certificate authority, and use neo4j+s://. For example, in Python (pip install neo4j):

from neo4j import GraphDatabase

driver = GraphDatabase.driver("neo4j+ssc://<vm-ip>:7687", auth=("neo4j", "<NEO4J_PASSWORD>"))
with driver.session() as session:
    print(session.run("MATCH (n) RETURN count(n) AS nodes").single()["nodes"])
driver.close()

Create a separate user for each application instead of sharing the neo4j administrator (Community Edition users all have full access, so keep the list short), for example in cypher-shell or Neo4j Browser connected to the system database: CREATE USER app SET PASSWORD 'a-long-random-password' CHANGE NOT REQUIRED;

To change the neo4j password, run ALTER CURRENT USER SET PASSWORD FROM 'current-password' TO 'new-password'; in Neo4j Browser or cypher-shell, then update /root/neo4j-credentials.txt yourself (it is not rewritten).

Step 9: Serve under your own DNS name, and tune memory

Neo4j advertises one address to its clients (the Bolt routing table and Neo4j Browser use it). On first boot it is the VM's public IP address from the Azure metadata service (or its private IP when it has no public IP). After you point a DNS record at the VM, set it and issue a new certificate for that name:

sudo neo4j-set-hostname neo4j.<your-domain>

This regenerates the self-signed certificate (replace it with your own CA-issued certificate for production by putting private.key and public.crt in /var/lib/neo4j/certificates/cloudimg/ and restarting Neo4j), records the name in /etc/cloudimg/neo4j-hostname and restarts Neo4j.

Neo4j's memory recommendation for this VM size can be printed with:

sudo -u neo4j neo4j-admin server memory-recommendation 2>/dev/null | grep -E '^server\.memory'

Copy the server.memory.heap.initial_size, server.memory.heap.max_size and server.memory.pagecache.size lines into /etc/neo4j/neo4j.conf and run sudo systemctl restart neo4j.

Security notes

LOAD CSV and the metadata service. LOAD CSV reads data from files and URLs on the server. On this image a file:/// URL is resolved inside /var/lib/neo4j/import (copy your CSV files there with sudo cp data.csv /var/lib/neo4j/import/), so a query cannot read other files on the VM:

export NEO4J_PASSWORD="$(sudo sed -n 's/^NEO4J_PASSWORD=//p' /root/neo4j-credentials.txt)"
echo "LOAD CSV FROM 'file:///etc/shadow' AS row RETURN row LIMIT 1;" | \
  cypher-shell -a bolt+ssc://localhost:7687 -u neo4j --format plain 2>&1 | head -2 || true

Neo4j refuses it (22N43: ... unable to load external resource). The Neo4j service is also denied the Azure Instance Metadata Service and the wire server by a systemd IPAddressDeny rule, so a LOAD CSV FROM 'http://169.254.169.254/...' never reaches it. You can prove it from inside the service's own control group (the Azure Linux Agent, which is not in that group, keeps its access):

CG=$(systemctl show -p ControlGroup --value neo4j.service)
sudo bash -c "echo \$\$ > /sys/fs/cgroup${CG}/cgroup.procs; exec cloudimg-egress-probe neo4j.service"

It prints verdict=BLOCKED, with loopback reachable and the metadata addresses timing out:

The neo4j.service IPAddressDeny rule, an empty plugins directory, LOAD CSV of /etc/shadow refused, and the egress probe inside neo4j.service reporting the metadata service and wire server blocked

Plugins. No plugin ships, and /etc/dpkg/dpkg.cfg.d/cloudimg-neo4j-no-plugins keeps package upgrades from restoring the APOC, Graph Data Science and GenAI jars. If you decide you need a plugin, that is your own judgement about its security: plugins run inside the database with its permissions, and procedures such as APOC's apoc.load.* read files and URLs. To add one, download the plugin version that matches Neo4j 2026.10.1 from its official release page, copy the jar into /var/lib/neo4j/plugins/ (owned by neo4j), allow only the procedures you need with dbms.security.procedures.allowlist in /etc/neo4j/neo4j.conf, and restart Neo4j. The service sandbox and the metadata block still apply to a plugin.

The Neo4j package signing key expires on 31 December 2026. Neo4j's repository is signed with key 1EEF B876 7D49 24B8 6EAD 08A4 59D7 00E4 D37F 5F19 (Neo4j Admins), which expires on 2026-12-31. Nothing on this image needs the repository to boot or to run Neo4j, and Ubuntu's own security updates are unaffected: after that date apt-get update reports an error for the Neo4j repository (and exits non-zero), but Ubuntu's package lists still refresh and unattended security upgrades keep running, until you load Neo4j's renewed key. When Neo4j publishes it, refresh the key and check its fingerprint and expiry:

curl -fsSL https://debian.neo4j.com/neotechnology.gpg.key -o /tmp/neo4j.key
gpg --show-keys --with-colons /tmp/neo4j.key | awk -F: '/^fpr:/{print "fingerprint " $10; exit} /^pub:/{system("date -u -d @" $7 " +\"expires %Y-%m-%d\"")}'

If the fingerprint is the one above, install it with sudo gpg --dearmor --yes -o /usr/share/keyrings/neo4j-archive-keyring.gpg /tmp/neo4j.key and run sudo apt-get update. If Neo4j announces a different key, verify it against the instructions on https://debian.neo4j.com before installing it.

When you have finished with the example graph, remove it:

export NEO4J_PASSWORD="$(sudo sed -n 's/^NEO4J_PASSWORD=//p' /root/neo4j-credentials.txt)"
echo "MATCH (n) WHERE n:Person OR n:Topic DETACH DELETE n RETURN count(*) AS removed;" | \
  cypher-shell -a bolt+ssc://localhost:7687 -u neo4j --format plain
rm -f /tmp/neo4j.crt /tmp/neo4j.key

Keep your VM patched (sudo apt-get update && sudo apt-get upgrade; Neo4j updates come from the same official repository), back up your databases with neo4j-admin database dump while Neo4j is stopped, keep the neo4j password safe, and expose ports 7473 and 7687 only to the networks that need them.

Server components

Component Path
Configuration /etc/neo4j/neo4j.conf
Data directory /var/lib/neo4j/data
Import directory (LOAD CSV) /var/lib/neo4j/import
Plugins directory (empty) /var/lib/neo4j/plugins
Certificate and key /var/lib/neo4j/certificates/cloudimg/
Logs /var/log/neo4j/
Credentials file /root/neo4j-credentials.txt (mode 0600)
First-boot service neo4j-firstboot.service (/usr/local/sbin/neo4j-firstboot.sh)
Hostname and certificate helper /usr/local/sbin/neo4j-set-hostname

Support and licensing

Neo4j Community Edition is licensed under the GNU General Public License version 3. This image ships Community only; Neo4j Enterprise requires a commercial licence from Neo4j, Inc. cloudimg provides 24/7 technical support for this image by email, 365 days a year, with a 24-hour response SLA: support@cloudimg.co.uk.

Neo4j is a registered trademark of Neo4j, Inc. cloudimg is not affiliated with or endorsed by Neo4j, Inc.