> ## 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.

# Single-server deployment

> Deploy the live adapter behind Caddy with Auth0, PostgreSQL, Redis, and TLS-enabled Vault.

# Deploy the live neobank on one server

The server Compose path publishes only Caddy on ports 80 and 443. PostgreSQL, both Redis
instances, Vault, FastAPI, the worker, scheduler, and web service remain private.

## Configure the environment

Run the Auth0 reconciliation from a trusted administration machine first. The resulting
`.env.auth0` contains both runtime application credentials and Management API credentials.
Export only the runtime settings to a temporary file:

```bash theme={null}
umask 077
grep -E '^(NEOBANK_APP_NAME|NEOBANK_APP_NAME_AR|NEOBANK_APP_MARK|NEOBANK_PUBLIC_ORIGIN|NEOBANK_API_ORIGIN|NEOBANK_AUTH0_DOMAIN|NEOBANK_AUTH0_CLIENT_ID|NEOBANK_AUTH0_CLIENT_SECRET|NEOBANK_AUTH0_AUDIENCE|NEOBANK_AUTH0_CLAIM_NAMESPACE|NEOBANK_AUTH0_OPS_CLIENT_ID|NEOBANK_AUTH0_OPS_CLIENT_SECRET|NEOBANK_PROBLEM_TYPE_BASE)=' \
  .env.auth0 > .env.runtime
```

Transfer `.env.runtime` with Secure Copy Protocol (SCP). Set `server` to the SSH account and
host. Set `server_dir` to the repository directory on that server:

```bash theme={null}
server=deploy_user@server.example
server_dir=/srv/neobank-wrapper
scp .env.runtime "$server:$server_dir/.env.runtime"
```

Do not transfer `AUTH0_MANAGEMENT_CLIENT_ID`, `AUTH0_MANAGEMENT_CLIENT_SECRET`, or
`AUTH0_OPERATOR_ROLE_ID`.

On a new server, create the target environment and restrict both files before writing
secrets:

```bash theme={null}
cp .env.server.example .env.server
chmod 600 .env.server .env.runtime
```

Set `CADDY_ACME_EMAIL` in `.env.server`, then prepare the deployment:

```bash theme={null}
./scripts/prepare_server.sh .env.server .env.runtime
```

The script copies the runtime Auth0 and branding values, sets `NEOBANK_HOST` from the
public origin, generates distinct database, metrics, and Vault secrets on a new deployment,
and creates or validates the Vault TLS files. It retains already prepared server secrets on
subsequent runs. Delete the server copy after the script succeeds:

```bash theme={null}
rm -f .env.runtime
```

After the server starts successfully, delete the administration-machine copy with the same
command.

<Warning title="Existing server environments">
  Do not replace an existing `.env.server` with the example file. Back it up in the protected
  secret store, confirm it contains `# NEOBANK_PREPARED_PROJECT=perflo-neobank-server`, and run
  the same preparation command. The script retains the provisioned database, metrics, and Vault
  secrets. If the marker is absent, stop and reconcile the existing environment and volumes
  before adopting it; do not generate new state-service credentials over live volumes.
</Warning>

Keep `NEOBANK_PERFLO_ADAPTER=live` and all five reviewed Perflo origins unchanged. See
[Configure a dedicated Auth0 tenant](/deploy/auth0) for the tenant-side procedure.

The edge network is pinned to `172.30.255.0/24`, Caddy to `172.30.255.2`, and the
API to `172.30.255.3`. Uvicorn trusts forwarded client addresses only from the Caddy IP.
If that subnet conflicts with the host, change the subnet, both static addresses, and
`NEOBANK_TRUSTED_PROXY_IPS` together in a reviewed deployment override; never widen the
trusted proxy setting to all private networks.

Compose isolates sessions, tasks, PostgreSQL, and Vault on separate internal networks.
The API joins edge, sessions, database, and Vault; the worker joins tasks, database, Vault,
and a dedicated outbound-only provider bridge; the scheduler joins only tasks. The worker
and scheduler cannot reach customer session Redis, and Caddy and web cannot reach data
planes.

## Understand Vault TLS

The preparation script creates `ca.pem`, `server.pem`, and `server-key.pem` in the directory
named by `NEOBANK_VAULT_TLS_DIR`. The certificate covers `DNS:vault` and `IP:127.0.0.1`.
Compose mounts the CA into the API and worker; the scheduler has no Vault network or
credential. Stop the server before replacing an invalid or expiring certificate.

The bootstrap sidecar initializes and unseals Vault, enables Transit, creates the derived `neobank-perflo-credentials` key, and creates the restricted application token. Protect the TLS key, Vault data, and bootstrap state as one recovery unit.

## Start the server

Validate and start:

```bash theme={null}
make server-config
make caddy-config
make server-up
```

The one-shot `migrate` service applies pending migrations before the API starts — an empty database is initialized and an existing one is upgraded in place. Nothing ever deletes an existing database.

## Verify the boundary

Check health, readiness, public branding, and the customer login redirect:

```bash theme={null}
curl --fail https://neobank.example/health/live
curl --fail https://neobank.example/health/ready
curl --fail https://neobank.example/v1/public-config
curl --silent --output /dev/null --dump-header - \
  https://neobank.example/v1/session/login
```

Replace `neobank.example` with the configured host. The public configuration must contain
the configured application name and mark and no Auth0 settings. The login response must be
an HTTP redirect whose `Location` uses the configured Auth0 custom domain. Complete one
customer login and one operator login; operator login must require MFA.

Confirm an unauthenticated customer route returns `401`. Caddy removes caller-supplied debug
identity headers and preserves bearer authorization for OAuth-authenticated agents and server
integrations. Production documentation routes require operator authentication.

Run the controlled live canary before production traffic. Stop containers without deleting volumes by running `make server-down`. Revoke every Perflo device before deleting PostgreSQL or Vault state.
