# Deploy

Deploy the Nuts node and, optionally, Nuts Admin. Put the node behind a reverse proxy with the right TLS certificates, and check that it's reachable.

# Deploy the Nuts node

There are two ways to run the Nuts part of your system. Choose one:

| Option | What it is | When to choose it |
|---|---|---|
| **Nuts node** (default) | The Nuts node on its own. You build the integration with your EHR yourself, following this book. | You want full control, or you already integrate with the Nuts node |
| **Nuts Knooppunt** | Has an embedded Nuts node, plus components for integrating with the MinVWS Generic Functions (Generieke Functies), such as a policy decision point (PDP) for access decisions and Mitz integration | You want more ready-made parts, or you integrate with the Generic Functions. See the Knooppunt section below |

One Nuts node per vendor (and per environment) hosts all of the vendor's care organizations.

## Nuts node

The [Nuts node documentation](https://nuts-node.readthedocs.io/en/latest/) describes deployment in detail. Start with the [recommended deployment](https://nuts-node.readthedocs.io/en/latest/pages/deployment/recommended-deployment.html) and [running on Docker](https://nuts-node.readthedocs.io/en/latest/pages/deployment/docker.html).

### Install

The Nuts node is published as:

| Form | Where |
|---|---|
| Docker image | `nutsfoundation/nuts-node` on [Docker Hub](https://hub.docker.com/r/nutsfoundation/nuts-node/tags) |
| Helm chart, for Kubernetes | Helm repository `https://nuts-foundation.github.io/nuts-node`, chart `nuts-node-chart` ([source](https://github.com/nuts-foundation/nuts-node/tree/master/charts/nuts-node)) |
| Binaries (Linux, amd64 and arm64) | [GitHub releases](https://github.com/nuts-foundation/nuts-node/releases) |

Use a fixed version (e.g. `nutsfoundation/nuts-node:<version>`), not `latest`. For the latest version, see the [GitHub releases](https://github.com/nuts-foundation/nuts-node/releases). Docker images are signed; see [verifying image signatures](https://nuts-node.readthedocs.io/en/latest/pages/deployment/docker.html#verifying-image-signatures).

### Configuration

The settings you need for a spec v2 use case. Each can be set in `nuts.yaml` or as an environment variable (`NUTS_` + the name in capitals, with `_` instead of `.`, e.g. `NUTS_HTTP_INTERNAL_ADDRESS`).

| Setting | Value | Why |
|---|---|---|
| `url` | Your node's public URL, e.g. `https://nuts.example.com` | Used in your did:web DIDs and OAuth2 URLs. Must be HTTPS. |
| `strictmode` | `true` (default) | Forbids insecure settings. Only turn it off on development or test systems without production data. |
| `didmethods` | `web` | Spec v2 only uses did:web (see "Create a subject"). |
| `http.internal.address` | e.g. `:8081` | The internal API. Default `127.0.0.1:8081`, which can't be reached from outside the container. |
| `storage.sql.connection` | Connection string to PostgreSQL, MySQL or SQL Server (including Azure SQL) | The default, SQLite, isn't meant for production. |
| `crypto.storage` | `vaultkv` (HashiCorp Vault) or `azure-keyvault` | Where private keys are stored. `fs` (on disk) is only for development. |
| `discovery.definitions.directory` | Directory with the Discovery Service definitions | See chapter "Configure the node for a use case". |
| `policy.directory` | Directory with the policy definitions | See chapter "Configure the node for a use case". |

### Network

- Put a reverse proxy in front of the node that terminates TLS. See "Endpoints and TLS certificates" for which endpoints to expose, and with which certificate.
- Never expose the internal API (`:8081`) to the internet.
- If the node runs on a subpath (e.g. `https://example.com/nuts`), rewrite the `.well-known` URLs. See "Configure url rewrite for OAuth".

### Check that it works

- `GET <internal Nuts interface>/health` returns `200 OK` with status `UP`.
- After creating your first subject, check that its DID resolves from the internet (see "Create a subject").

## Nuts Knooppunt

Knooppunt has an embedded Nuts node, so the steps in this book still apply. It also has a policy decision point (PDP) that helps you decide whether to allow an inbound query (see "Validate the access token").

See the [Knooppunt documentation](https://github.com/nuts-foundation/nuts-knooppunt/tree/main/docs), in particular:

- [Deployment](https://github.com/nuts-foundation/nuts-knooppunt/blob/main/docs/DEPLOYMENT.md)
- [Configuration](https://github.com/nuts-foundation/nuts-knooppunt/blob/main/docs/CONFIGURATION.md)
- [Integration](https://github.com/nuts-foundation/nuts-knooppunt/blob/main/docs/INTEGRATION.md), including the PDP
- An example [docker-compose.yml](https://github.com/nuts-foundation/nuts-knooppunt/blob/main/docker-compose.yml)

# Endpoints and TLS certificates

Which endpoints you expose, to whom, and with which TLS certificate. This repeats the [recommended deployment](https://nuts-node.readthedocs.io/en/latest/pages/deployment/recommended-deployment.html) for spec v2 use cases; see there for details.

## Nuts node endpoints

Your reverse proxy terminates TLS for the public endpoints.

| Endpoint | Interface | Who uses it | Protection |
|---|---|---|---|
| `/iam` | Public (`:8080`) | Other parties resolving your did:web DID documents | HTTPS with a **publicly trusted** server certificate |
| `/.well-known` | Public (`:8080`) | Other parties: DID documents, OAuth2 metadata | HTTPS with a **publicly trusted** server certificate |
| `/oauth2` | Public (`:8080`) | Other Nuts nodes requesting access tokens from you | HTTPS with a **publicly trusted** server certificate |
| `/statuslist` | Public (`:8080`) | Parties checking whether credentials you issued are revoked | HTTPS with a **publicly trusted** server certificate |
| `/discovery` | Public (`:8080`) | Only if your node runs a Discovery Service server | HTTPS with a **publicly trusted** server certificate |
| `/internal` | Internal (`:8081`) | Your own applications, Nuts Admin | Never public: restrict access through network separation |
| `/status`, `/status/diagnostics`, `/health`, `/metrics` | Internal (`:8081`) | Monitoring, health checks | Restrict access through network separation |
| `/n2n`, `/public`, gRPC (`:5555`) | — | Spec v1 (did:nuts) only | Don't expose them |

## Your data endpoints

Protect your FHIR API (or other data API) with mutual TLS (mTLS) based on PKIoverheid certificates: both your server and the calling vendor's client authenticate with a PKIoverheid certificate. Check the use case's IG for what's allowed in test environments.

## Don't mix up the certificates

| Certificate | Used for |
|---|---|
| UZI server certificate | Creating the care organization's X509Credential (see "Get an X509Credential from a UZI certificate") |
| Publicly trusted TLS certificate | The Nuts node's public endpoints |
| PKIoverheid certificate | mTLS on your data endpoints |

# Deploy admin app

Nuts Admin is an optional web application for managing your Nuts node by hand: create subjects, load and issue credentials, and register on Discovery Services. It's useful for testing and for small numbers of care organizations. For many care organizations, integrate with the node's internal API instead, as described in the rest of this book.

## Run it

```shell
docker run -p 1305:1305 -e NUTS_NODE_ADDRESS=http://nutsnode:8081 nutsfoundation/nuts-admin:<version>
```

- `NUTS_NODE_ADDRESS` points to the node's **internal** API.
- Nuts Admin listens on port `1305` (change it with `PORT`).
- For the latest version, see the [tags on Docker Hub](https://hub.docker.com/r/nutsfoundation/nuts-admin/tags).

See the [Nuts Admin README](https://github.com/nuts-foundation/nuts-admin) for all settings.

## Secure it

Nuts Admin passes requests on to the node's internal API. Anyone who can reach Nuts Admin can manage your node.

- Turn on user login with OpenID Connect (`NUTS_OIDC_ENABLED=true`, plus the settings in the README), or
- restrict access to Nuts Admin in another way, e.g. network separation.

Never expose Nuts Admin to the internet without login.

# Configure url rewrite for OAuth

Other parties look up your node's OAuth2 metadata at a `.well-known` URL, which always starts at the root of the domain ([RFC 8615](https://www.rfc-editor.org/rfc/rfc8615.html)). If your node runs at the root of its own (sub)domain, e.g. `https://nuts.example.com`, you don't need to do anything.

If the node runs on a subpath, e.g. `https://example.com/nuts`, your reverse proxy must forward the OAuth2 metadata URL to the node. Example for a node on `/nuts`:

| Other parties request | Forward to the node as |
|---|---|
| `https://example.com/.well-known/oauth-authorization-server/nuts/oauth2/<subject ID>` | `/.well-known/oauth-authorization-server/oauth2/<subject ID>` |

In short: remove the subpath (`/nuts`) after the `.well-known/...` part.

The node serves all its other endpoints at the root too (`/iam`, `/oauth2`, `/statuslist`, ...). So your proxy must also remove `/nuts` from those, e.g. forward `https://example.com/nuts/oauth2/...` to `/oauth2/...`.

## NGINX

```nginx
location /.well-known/oauth-authorization-server/nuts/ {
    proxy_pass http://nuts-node:8080/.well-known/oauth-authorization-server/;
}
```

## Azure Application Gateway

```
path: '/.well-known/oauth-authorization-server/nuts'
rewriteCondition: '/.well-known/oauth-authorization-server/nuts(.*)'
rewriteRule: '/.well-known/oauth-authorization-server{var_uri_path_1}'
```

## Check that it works

From outside your network, open:

```
https://example.com/.well-known/oauth-authorization-server/nuts/oauth2/<subject ID>
```

It should return JSON with your node's OAuth2 metadata, e.g. an `issuer` of `https://example.com/nuts/oauth2/<subject ID>`.