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

# PostgreSQL & Redis

> Provision PostgreSQL 17 and separate Redis authorities for sessions and asynchronous tasks.

# PostgreSQL & Redis

PostgreSQL 17 is the primary state store. Separate Redis 7 authorities back customer
sessions and Celery task transport. All three are mandatory for every secure runtime; the
local stack ships two isolated Redis containers.

## PostgreSQL 17

PostgreSQL stores customers, provider bindings, operations, idempotency records, outbox events, beneficiaries, estimates, mandates, cards, and audit events. Live mode requires PostgreSQL; SQLite remains limited to tests and fake development.

Set `NEOBANK_DATABASE_URL` with the `postgresql+psycopg` scheme:

```text theme={null}
NEOBANK_DATABASE_URL=postgresql+psycopg://neobank:<password>@<host>:5432/neobank
```

### Run migrations

Alembic owns schema state. On Compose, a one-shot `migrate` service runs before the API starts:

```text theme={null}
alembic upgrade head
```

On Kubernetes, a `pre-install,pre-upgrade` hook Job runs `alembic upgrade head` before the workloads roll (see [Kubernetes](deploy/kubernetes)). The application containers themselves never run migrations — the API and worker instead refuse to start when the database's migration revision is stale.

## Redis 7

The API uses `NEOBANK_REDIS_URL` for sessions and rate limits. The worker and scheduler use
`NEOBANK_TASK_QUEUE_URL` for the Celery broker and result backend. The task workloads never
receive the session Redis URL. The local stack runs both Redis instances with append-only-file
persistence (`--appendonly yes`).

Set `NEOBANK_REDIS_URL`:

```text theme={null}
NEOBANK_REDIS_URL=redis://<host>:6379/0
NEOBANK_TASK_QUEUE_URL=redis://<different-host>:6379/0
```

Use separate instances or Redis ACL users that cannot read or write each other's keys.
Different database numbers on the same unrestricted Redis identity do not prevent a
compromised scheduler from forging customer sessions.

## Kubernetes namespace assumption

The Helm NetworkPolicy restricts each workload to its required namespace and port. The
defaults assume in-cluster services on standard ports:

| Service       | Namespace default  | Port   |
| ------------- | ------------------ | ------ |
| PostgreSQL    | `neobank-data`     | `5432` |
| Session Redis | `neobank-sessions` | `6379` |
| Task Redis    | `neobank-tasks`    | `6379` |
| Vault         | `vault`            | `8200` |

Override `networkPolicy.databaseNamespace`, `networkPolicy.sessionRedisNamespace`,
`networkPolicy.taskRedisNamespace`, or `networkPolicy.vaultNamespace` if the in-cluster
topology differs. Managed external services require additional caller-supplied policies
restricted to their exact address ranges. See [Kubernetes](deploy/kubernetes).

## Concurrency test database

The Postgres concurrency test suite runs against a dedicated database named `neobank_test` — the tests refuse any other database name. Set `NEOBANK_TEST_DATABASE_URL` and run:

```bash theme={null}
make integration
```

The target fails fast if `NEOBANK_TEST_DATABASE_URL` is unset, then runs the tests marked `integration`. Create the `neobank_test` database in your Postgres instance before invoking it.
