> ## Documentation Index
> Fetch the complete documentation index at: https://docs.controlplane.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Neo4j

> Neo4j Community Edition graph database queried with Cypher, deployed as a single server on a persistent volume with daily snapshots, Bolt, the HTTP Query API, Neo4j Browser, and APOC. Deploys into an existing single-location GVC; a credentials secret must exist first.

## Overview

Neo4j is a property graph database queried with Cypher. This template deploys a single Neo4j Community Edition server on a persistent, snapshotted volume. It serves Bolt to applications in the GVC and the bundled Neo4j Browser and HTTP Query API, privately by default or on a public HTTPS endpoint, with the APOC Core procedures loaded.

> [!NOTE]
>
> This template does not create a GVC. It deploys into an existing GVC that you already have, and that GVC should have exactly one location (see [Prerequisites](#prerequisites)).

### What Gets Created

| Resource | Name | Purpose | Optional |
| - | - | - | - |
| Workload | `RELEASE_NAME-neo4j` | Neo4j server (stateful, 1 replica): Neo4j Browser and HTTP Query API on `7474`, Bolt on `7687` | — |
| Volume set | `RELEASE_NAME-neo4j-data` | Data volume mounted at `/data` (stores and transaction logs), with scheduled snapshots | — |
| Identity | `RELEASE_NAME-neo4j-identity` | Workload identity used to read your credentials secret | — |
| Policy | `RELEASE_NAME-neo4j-policy` | Grants the identity `reveal` on exactly the secret named in `auth.credentialsSecretName` | — |

Neo4j clustering and online backup are Enterprise-only features, so this is a single server with no failover. Nothing is reachable from the internet unless you turn on `publicAccess`, and Bolt always stays private.

## Prerequisites

**1. Create the credentials secret**

Create a **dictionary** secret with the key `password`. The password must be at least 8 characters and must not contain `/`. The user is always `neo4j`.

```bash theme={null}
cpln secret create-dictionary --name SECRET_NAME \
  --entry password="$(openssl rand -hex 24)"
```

Read the password back at any time:

```bash theme={null}
cpln secret reveal SECRET_NAME -o yaml
```

The password is applied only on the first start, when the database is created. To change it later, see [Changing the Password](#changing-the-password).

Set `auth.credentialsSecretName` to this name.

**2. Use a GVC with exactly one location**

Each location of the GVC runs its own replica with its own volume, so every extra location would be a separate, empty Neo4j. Check the GVC's locations under `spec.staticPlacement.locationLinks`:

```bash theme={null}
cpln gvc get GVC_NAME -o yaml
```

> [!WARNING]
>
> The credentials secret must exist **before** you install. If it does not, the deployment waits silently: `cpln logs` returns nothing, and the only place the missing secret is named is `status.versions[].message` in `cpln workload get-deployments RELEASE_NAME-neo4j --gvc GVC_NAME -o yaml`. Create the secret and the deployment recovers on its own after several minutes, or run `cpln workload force-redeployment RELEASE_NAME-neo4j --gvc GVC_NAME` to skip the wait.

## Installation

The only required value is the name of the credentials secret from [Prerequisites](#prerequisites):

```bash theme={null}
cpln helm install RELEASE_NAME oci://ghcr.io/controlplane-com/templates/neo4j \
  --version 1.0.0 --gvc GVC_NAME \
  --set auth.credentialsSecretName=SECRET_NAME
```

To install, follow the instructions for your preferred method:

> **[UI](/template-catalog/install-manage/ui)**
>
> Browse, install, and manage templates visually

> **[CLI](/template-catalog/install-manage/cli)**
>
> Manage templates from your terminal

> **[Terraform](/template-catalog/install-manage/terraform)**
>
> Declare templates in your Terraform configurations

> **[Pulumi](/template-catalog/install-manage/pulumi)**
>
> Declare templates in your Pulumi programs

## Configuration

### Image

```yaml theme={null}
# Neo4j Community Edition (GPLv3). Enterprise images are refused: they need a paid license.
image: neo4j:2026.09.0-community
```

The chart refuses to render with any image whose name contains `enterprise`.

### Credentials

```yaml theme={null}
auth:
  # REQUIRED PREREQUISITE SECRET — CREATE IT BEFORE YOU INSTALL.
  # A dictionary secret with the key `password` (8+ characters, no `/`); the user is always `neo4j`.
  # If it does not exist at install time the deployment waits silently and `cpln logs` shows nothing.
  # Used only on the FIRST start; to change the password later, see the README.
  credentialsSecretName: my-neo4j-credentials
```

The password in the secret is used only when the database is first created. On every later start the password stored in the database is kept, so editing the secret changes nothing in Neo4j. See [Changing the Password](#changing-the-password).

### Resources

```yaml theme={null}
resources:
  minCpu: 250m
  maxCpu: 1000m   # at most 4× minCpu
  minMemory: 2Gi
  maxMemory: 2Gi  # keep heapSize + pageCacheSize to about 3/4 of this
```

### Neo4j Memory and Plugins

```yaml theme={null}
neo4j:
  heapSize: 512m      # JVM heap (initial = max), Neo4j size syntax: 512m, 2g
  pageCacheSize: 512m # graph store cache outside the heap
  apoc: true          # load the bundled APOC Core procedures
```

Raise these together with `resources.maxMemory`. For a production-sized graph, for example: `maxMemory: 8Gi`, `heapSize: 3g`, `pageCacheSize: 3g`, and a larger volume with autoscaling on. Size the page cache toward the size of your store. APOC is on by default because graph-store integrations such as LangChain and LlamaIndex use it.

### Storage

```yaml theme={null}
volumeset:
  capacity: 10 # initial capacity in GiB (minimum 10)
  autoscaling:
    enabled: false # grow the volume automatically as it fills
    maxCapacity: 100 # GiB ceiling when autoscaling
    minFreePercentage: 10
    scalingFactor: 1.2
  snapshots:
    schedule: "0 3 * * *" # daily at 03:00 UTC; at most hourly; "" turns scheduled snapshots off
    retentionDuration: 7d # how long each snapshot is kept
```

`capacity` only sizes a new volume. To let an existing volume grow, turn on `volumeset.autoscaling` and set `maxCapacity` to the ceiling you are willing to pay for. Snapshots are covered in [Backup and Restore](#backup-and-restore).

### Public Access

```yaml theme={null}
publicAccess:
  enabled: false # Neo4j Browser and the HTTP API on the workload's public HTTPS endpoint; Bolt stays private
```

Install privately first and log in through a port-forward (see [Connecting](#connecting)), then turn this on with `helm upgrade`. Logins are protected by the `neo4j` password, and Neo4j locks an account for 5 seconds after 3 wrong attempts. Bolt is never public: drivers that need Bolt connect from inside the GVC or through a port-forward. A firewall change can take a few minutes to take effect.

> [!WARNING]
>
> Always use the `https://` address. The public endpoint also answers plain `http://`, which sends the password unencrypted.

### Internal Access

```yaml theme={null}
internalAccess:
  type: same-gvc # none, same-gvc, same-org or workload-list
  workloads: [] # extra allowed clients, used with same-gvc or workload-list
  # workloads:
  #   - //gvc/GVC_NAME/workload/WORKLOAD_NAME
```

| `internalAccess.type` | Description |
| - | - |
| `none` | No internal access; in-GVC traffic is refused. |
| `same-gvc` | Allow access from all workloads in the same GVC (default), plus any in `workloads`. |
| `same-org` | Allow access from all workloads in the same org. |
| `workload-list` | Allow access only from the workload links in `internalAccess.workloads`. |

Changing `internalAccess` restarts Neo4j, like any other configuration change.

## Connecting

| What | Address | Notes |
| - | - | - |
| Bolt, from a workload in the GVC | `bolt://RELEASE_NAME-neo4j.GVC_NAME.cpln.local:7687` | `neo4j://` works too; user `neo4j` |
| HTTP Query API, from a workload in the GVC | `http://RELEASE_NAME-neo4j.GVC_NAME.cpln.local:7474/db/neo4j/query/v2` | HTTP basic auth, user `neo4j` |
| Neo4j Browser, from your computer | `http://localhost:7474/browser/` through a port-forward (below) | Connect to `bolt://localhost:7687`; works with public access off |
| Neo4j Browser, with `publicAccess` on | `https://CANONICAL_ENDPOINT/browser/?dbms=https://CANONICAL_ENDPOINT:443` | `CANONICAL_ENDPOINT` is the host of `status.canonicalEndpoint` from `cpln workload get RELEASE_NAME-neo4j --gvc GVC_NAME -o yaml` |
| HTTP Query API, with `publicAccess` on | `https://CANONICAL_ENDPOINT/db/neo4j/query/v2` | HTTPS only; Bolt is not public |
| Credentials | `cpln secret reveal SECRET_NAME -o yaml` | User `neo4j` and the secret's `password` key (until you change the password in Neo4j) |

Open Neo4j Browser from your machine with a port-forward of both the Browser and Bolt ports:

```bash theme={null}
cpln port-forward RELEASE_NAME-neo4j 7474:7474 7687:7687 --gvc GVC_NAME
```

Then open `http://localhost:7474/browser/`. The connect form pre-fills `localhost:7687`; log in as `neo4j` with the password from your secret.

With `publicAccess` on, open the public Browser link with the `?dbms=` parameter shown in the table. If you open `/browser/` without it, set the protocol to `https://` and change the pre-filled `:7687` to `:443` before you click Connect, otherwise Connect hangs because port 7687 is not public.

## Operations

### Backup and Restore

Backups are volume set snapshots: daily at 03:00 UTC by default, each kept for `volumeset.snapshots.retentionDuration`. They stay with the volume set in Control Plane and are not copied off-site. Uninstalling deletes the volume set along with every snapshot you can restore. Neo4j Community cannot back up a running database, so there is no separate backup job.

List the snapshots, or take one on demand (for example before an upgrade):

```bash theme={null}
cpln volumeset snapshot get RELEASE_NAME-neo4j-data --gvc GVC_NAME
cpln volumeset snapshot create RELEASE_NAME-neo4j-data --gvc GVC_NAME --snapshot-name SNAPSHOT_NAME
```

To restore, restore a snapshot onto the volume. Neo4j restarts on the restored data by itself:

```bash theme={null}
cpln volumeset snapshot restore RELEASE_NAME-neo4j-data --gvc GVC_NAME \
  --snapshot-name SNAPSHOT_NAME --location LOCATION --volume-index 0
```

`LOCATION` is the GVC's single location (for example `aws-us-east-1`). Everything written after the snapshot is lost, and the `neo4j` password is the one that was in use when the snapshot was taken.

### Changing the Password

The secret is read only when the database is first created, so the password is changed inside Neo4j:

1. In Neo4j Browser, switch to the system database with `:use system`, then run:

   ```cypher theme={null}
   ALTER CURRENT USER SET PASSWORD FROM 'OLD_PASSWORD' TO 'NEW_PASSWORD'
   ```

2. Update the `password` key of your secret so it matches the new password:

   ```bash theme={null}
   cpln secret reveal SECRET_NAME -o yaml > secret.yaml
   # edit only the password key in secret.yaml
   cpln apply -f secret.yaml
   cpln secret reveal SECRET_NAME -o yaml
   ```

3. Force a redeployment of every application workload that reads the password from the secret. A running workload does not pick up a changed secret on its own:

   ```bash theme={null}
   cpln workload force-redeployment WORKLOAD_NAME --gvc GVC_NAME
   ```

The new password stays in force across Neo4j restarts and upgrades.

### Scaling and Availability

* **Single server by design.** There is no `replicas` knob: clustering is a Neo4j Enterprise feature.
* **Every upgrade or configuration change restarts the only server.** Neo4j is unavailable until the new replica is ready, so plan changes around your clients. Data on the volume survives.
* **Scale vertically.** Raise `resources.maxMemory` together with `neo4j.heapSize` and `neo4j.pageCacheSize`, and grow storage with `volumeset.autoscaling`.

### Troubleshooting

> **Deployment never becomes ready and cpln logs is empty**
>
> **Symptom:** The workload stays not ready and `cpln logs` returns no lines at all.
>
> **Cause:** The secret named in `auth.credentialsSecretName` does not exist. `status.versions[].message` in `cpln workload get-deployments RELEASE_NAME-neo4j --gvc GVC_NAME -o yaml` reads `The secret SECRET_NAME no longer exists. Workload updates are paused until the secret is added or the reference to the secret removed.`
>
> **Fix:** Create the secret as shown in [Prerequisites](#prerequisites). The deployment recovers on its own after several minutes, or run `cpln workload force-redeployment RELEASE_NAME-neo4j --gvc GVC_NAME`.

> **neo4j-start FATAL on the first start**
>
> **Symptom:** The container exits and restarts, and the logs show one of `neo4j-start: FATAL: the 'password' key of the credentials secret must be at least 8 characters`, `neo4j-start: FATAL: the password must not contain '/'` or `neo4j-start: FATAL: the password must not be 'neo4j'`.
>
> **Cause:** The password in the secret does not meet Neo4j's requirements, so the database is not created.
>
> **Fix:** Update the secret's `password` key to a valid value, then run `cpln workload force-redeployment RELEASE_NAME-neo4j --gvc GVC_NAME`.

> **Old password still works after editing the secret**
>
> **Symptom:** You changed the `password` key of the secret, but Neo4j still accepts only the old password.
>
> **Cause:** The secret is used only when the database is first created. On an existing database the logs show `neo4j-start: existing database in /data; keeping the password stored in it`.
>
> **Fix:** Change the password inside Neo4j as described in [Changing the Password](#changing-the-password).

> **Public Browser Connect hangs**
>
> **Symptom:** Neo4j Browser on the public endpoint stays on the connect screen after you click Connect.
>
> **Cause:** The connect form pre-filled port `7687`, which is not public. A wrong password over the `https://` protocol can also open the web browser's own sign-in prompt and leave the connect screen stuck.
>
> **Fix:** Open `https://CANONICAL_ENDPOINT/browser/?dbms=https://CANONICAL_ENDPOINT:443`, or set the protocol to `https://` and the port to `:443`. After a wrong password, reload the page and log in again.

> **Neo4j Browser hangs through port-forward**
>
> **Symptom:** Through `cpln port-forward`, Neo4j Browser goes blank or disconnected after some navigation, while the tunnel is still running.
>
> **Cause:** A web browser's reused connections through the tunnel stop responding.
>
> **Fix:** Restart the port-forward and fully quit the web browser, then open the Browser again.

> **Container restarts with reason OOMKilled**
>
> **Symptom:** Neo4j stops with no error in its logs, and `cpln workload get-deployments RELEASE_NAME-neo4j --gvc GVC_NAME -o yaml` shows restarts with reason `OOMKilled`.
>
> **Cause:** `neo4j.heapSize` plus `neo4j.pageCacheSize` leaves too little room under `resources.maxMemory` for the rest of the JVM.
>
> **Fix:** Keep heap plus page cache to about three quarters of `resources.maxMemory`, raising `maxMemory` if you need more of either.

> **Install fails at render time**
>
> **Symptom:** `cpln helm install` fails with a message starting `neo4j:`, for example `neo4j: image "neo4j:2026.09.0-enterprise" is Neo4j Enterprise Edition, which requires a paid license` or `neo4j: volumeset.capacity must be at least 10 (GiB)`.
>
> **Cause:** A value fails the chart's validation: an Enterprise image, a capacity below 10, an `autoscaling.maxCapacity` below `capacity`, a memory size not in Neo4j syntax, a snapshot schedule more frequent than hourly, or an unknown `internalAccess.type`.
>
> **Fix:** Correct the value named in the message and install again.

## Important Notes

* **Create the credentials secret before installing.** Without it the deployment waits silently with no logs.
* **The secret's password is used only on the first start.** Change the password with `ALTER CURRENT USER SET PASSWORD`, then update the secret and redeploy the apps that read it.
* **Use a single-location GVC.** Each extra location runs a separate, empty Neo4j.
* **Every upgrade or configuration change restarts the only server.** Neo4j is briefly unavailable each time.
* **Keep `heapSize` + `pageCacheSize` to about three quarters of `maxMemory`.** An over-committed container is OOM-killed.
* **With `publicAccess` on, always use `https://`.** Plain `http://` sends the password unencrypted, and Bolt is never public.
* **Uninstall deletes the volume set and its snapshots.** Your credentials secret is left untouched.

## External References

> **[Neo4j Docker Operations](https://neo4j.com/docs/operations-manual/current/docker/)**
>
> Running Neo4j from the official image

> **[Cypher Manual](https://neo4j.com/docs/cypher-manual/current/)**
>
> The Cypher query language reference

> **[Neo4j Browser](https://neo4j.com/docs/browser/)**
>
> Using the bundled Neo4j Browser

> **[APOC Core](https://neo4j.com/docs/apoc/current/)**
>
> The APOC procedures and functions loaded by default

> **[Neo4j Drivers](https://neo4j.com/docs/create-applications/)**
>
> Connecting applications over Bolt
