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

# Kubernetes

This page provides instructions on how to configure the Helm chart to install Hoop in any cloud provider.

## Quick Start

<Tabs>
  <Tab title="Standard Installation">
    This is the standard installation to evaluate Hoop. We recommend it for Proof of Concepts or testing environments.

    <Warning>
      On this installation, clients establishes connections without TLS, which is subject to interception.
      Make sure to deploy it over a secure network and use it only with non production resources.
    </Warning>

    <Steps>
      <Step title="Deploy the Gateway">
        ```sh theme={"dark"}
        VERSION=$(curl -s https://releases.hoop.dev/release/latest.txt)
        helm upgrade hoop --install oci://ghcr.io/hoophq/helm-charts/hoop-chart --version $VERSION \
          --namespace hoopdev --create-namespace \
          --set postgres.enabled=true \
          --set defaultAgent.enabled=true \
          --set dataMasking.enabled=true \
          --set dataMasking.analyzer.replicas=2 \
          --set 'config.POSTGRES_DB_URI=postgres://root:default-pwd@hoopgateway-pg/postgres?sslmode=disable' \
          --set config.API_URL=http://localhost:8009
        ```
      </Step>

      <Step title="Access it">
        1. Forward the hoopgateway service ports to your local machine to access the WebApp

        ```sh theme={"dark"}
        kubectl port-forward service/hoopgateway 8009:8009 8010:8010 -n hoopdev
        ```

        2. [Visit the Webapp at http://127.0.0.1:8009/login](http://127.0.0.1:8009/login)

        <Note>
          The default installation method install a Postgres database with Host mounted storage.
          In case the node is decommissioned, all data will be lost.

          For more durable setups, use a Persistent Volume by providing the option below:

          * `--set postgres.storageClassName=<your-storage-class>`
        </Note>
      </Step>
    </Steps>
  </Tab>

  <Tab title="Secure Installation (TLS)">
    This is production is recommended for production, it terminate the TLS directly on the gateway for all existent protocols.

    <Note>
      For the purpose of this quick start, we're going to generate certificate signed by a CA unstruted party with `openssl`. However,
      the process of installing and configuring should be the same when issuing certificates from trusted party organizations.

      Ensure you have all required certificate files from Step 1 if you've already issued them through your PKI provider.
    </Note>

    <Steps>
      <Step title="Obtain a certificate for your domain">
        1. Expose the domain name of the Hoop Gateway instance

        ```sh theme={"dark"}
        export DOMAIN_HOSTNAME=<your-hoop-gateway-domain-name-goes-here>
        ```

        2. Generate and sign your certificates with `openssl`

        ```sh theme={"dark"}
        mkdir ./tls && cd ./tls
        openssl genrsa -out ca.key 4096
        openssl req -x509 -new -nodes -key ca.key -sha256 -days 1826 -out ca.crt -subj '/CN=MyOrg CA/C=AT/ST=Vienna/L=Vienna/O=MyOrg'
        openssl req -new -nodes -out server.csr -newkey rsa:4096 -keyout server.key -subj '/CN=Gateway/C=AT/ST=Vienna/L=Vienna/O=MyOrg'
        # create a v3 ext file for SAN properties
        cat > server.v3.ext <<EOF
        authorityKeyIdentifier=keyid,issuer
        basicConstraints=CA:FALSE
        keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
        subjectAltName = @alt_names
        [alt_names]
        DNS.1 = $DOMAIN_HOSTNAME
        IP.1 = 127.0.0.1
        EOF

        openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 730 -sha256 -extfile server.v3.ext
        ```

        2. Join the Root Certificate Authority with your certificate

        ```sh theme={"dark"}
        cat server.crt ca.crt > server-full.crt
        ```

        <Note>
          When issuing certificates from a trusted party, make sure to include the Root and Intermediate certificates if they are provided.
        </Note>

        3. (optional) Add the Root CA to your system keychain

        This process is needed only when creating certificates from an unstrusted third party (openssl).
        It will make your system to trust the certificated issued by the Root Certificate Authority.

        **Make sure to have the following files in your local filesystem before proceeding to the next step:**

        * `server.crt` - File containing the server certificate
        * `ca.crt` - File containing the Root with Intermediate CA's if it's available
        * `server.key` - File containing the private key of your certificate
      </Step>

      <Step title="Generate the values.yaml file">
        After having the certificates in your working directory, encode the following files in base64 to generate the `values.yaml` file.
        The certificates will be used to serve the ports exposed in the gateway securely with TLS

        <Note>
          Make sure that the DNS name of your domain match with the certificate.
          In this example we'll be exposing all ports with a load balancer from AWS.
          Make sure to adapt this recipe to deploy a network load balancer in your own infra-structure with the proper annontations.
        </Note>

        1. Export the domain hostname and the certificate files

        ```sh theme={"dark"}
        export DOMAIN_HOSTNAME=<your-hoop-gateway-domain-name-goes-here>
        export TLS_KEY_ENC=$(cat server.key | base64)
        export TLS_CERT_ENC=$(cat server-full.crt | base64)
        ```

        2. Generate the `values.yaml` file

        ```sh theme={"dark"}
        cat - > values.yaml <<EOF
        # gateway base configuration
        config:
          POSTGRES_DB_URI: 'postgres://root:default-pwd@hoopgateway-pg/postgres?sslmode=disable'
          API_URL: "https://$DOMAIN_HOSTNAME"
          TLS_KEY: "base64://$TLS_KEY_ENC"
          TLS_CERT: "base64://$TLS_CERT_ENC"

        # uses a local Postgres database with Host mounted stored
        # use a distinct storage class name for more durable setups
        postgres:
          enabled: true
          storageClassName: null

        # enable data masking
        dataMasking:
          enabled: true
          mode: best-effort

          analyzer:
            replicas: 2

        # an agent will be deployed along with the gateway
        defaultAgent:
          enabled: true
          grpcScheme: grpcs
          grpcHost: 127.0.0.1:8010
          grpcSkipVerify: true
          imageTag: latest

        # exposes a network load balancer for all services
        proxyService:
          enabled: true
          annotations:
            service.beta.kubernetes.io/aws-load-balancer-type: nlb
            service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
          ports:
          - name: api
            port: 443
            targetPort: 8009
          - name: grpcs
            port: 8443
            targetPort: 8010
          - name: pgproxy
            port: 15432
            targetPort: 15432
          - name: sshproxy
            port: 12222
            targetPort: 12222
          - name: rdpproxy
            port: 13389
            targetPort: 13389
        EOF
        ```
      </Step>

      <Step title="Deploy it">
        ```sh theme={"dark"}
        VERSION=$(curl -s https://releases.hoop.dev/release/latest.txt)
        helm upgrade hoop \
          --install oci://ghcr.io/hoophq/helm-charts/hoop-chart --version $VERSION \
          --namespace hoopdev \
          --create-namespace \
          --values values.yaml
        ```
      </Step>

      <Step title="Access it">
        <Tabs>
          <Tab title="Via Public URL">
            1. Obtain the address of your load balancer by issuing the command below

            ```sh theme={"dark"}
            kubectl get svc -n hoopdev hoopgateway-proxy \
              -o jsonpath='{.status.loadBalancer.ingress[0].hostname}'
            ```

            2. Create a CNAME record for your `$DOMAIN_HOSTNAME` in your DNS pointing to this address.
            3. Visit the Webapp at `https://$DOMAIN_NAME/login`
          </Tab>

          <Tab title="Via Port Forward">
            If you want to validate the installation without exposing the gateway to the public internet,
            use the `kubectl` to port forward the ports into your local machine.

            ```sh theme={"dark"}
            kubectl port-forward service/hoopgateway 8009:8009 8010:8010 -n hoopdev
            ```

            Then [Visit the Webapp at http://127.0.0.1:8009/login](http://127.0.0.1:8009/login)

            <Note>
              To test the native protocols directly on the gateway, make sure to export the ports below:

              * `ssh/12222`
              * `postgres/15432`
              * `rdp/13389`
            </Note>
          </Tab>
        </Tabs>
      </Step>
    </Steps>
  </Tab>
</Tabs>

## Helm Install

To install the latest version in a new namespace (example: `hoopdev`). Issue the command below:

```bash theme={"dark"}
VERSION=$(curl -s https://releases.hoop.dev/release/latest.txt)
helm upgrade --install hoop \
  oci://ghcr.io/hoophq/helm-charts/hoop-chart --version $VERSION \
  -f values.yaml \
  --namespace hoopdev
```

### Overwriting or passing new attributes

It is possible to add new attributes or overwrite an attribute from a base `values.yaml` file.
In the example below a default agent is deployed as a sidecar container and with a using a specific version of the gateway.

```bash theme={"dark"}
helm upgrade --install hoop \
  oci://ghcr.io/hoophq/helm-charts/hoop-chart --version $VERSION \
  -f values.yaml \
  --set defaultAgent.enabled=true \
  --set image.gw.tag=1.45.0
```

## Database Configuration

Hoop uses Postgres as the backend storage of all data in the system.
It uses the schema `private` to create the tables of the system.
The command below creates a database and a user with privileges to access the database and the required schema.

```sql theme={"dark"}
CREATE DATABASE hoopdb;
CREATE USER hoopuser WITH ENCRYPTED PASSWORD 'my-secure-password';
-- switch to the created database
\c hoopdb
CREATE SCHEMA IF NOT EXISTS private;
GRANT ALL PRIVILEGES ON DATABASE hoopdb TO hoopuser;
GRANT ALL PRIVILEGES ON SCHEMA public to hoopuser;
GRANT ALL PRIVILEGES ON SCHEMA private to hoopuser;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO hoopuser;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA private TO hoopuser;
```

<Note>
  In case of using a password with special characters, make sure to url encode it properly when setting the connection string.
</Note>

Use these values to assemble the configuration for POSTGRES\_DB\_URI:

* `POSTGRES_DB_URI=postgres://hoopuser:<passwd>@<db-host>:5432/hoopdb`

<Tip>
  Make sure to include `?sslmode=disable` option in the Postgres connection string in case your database setup doesn't support TLS.
</Tip>

## Agent Deployment

<AccordionGroup>
  <Accordion title="values.yaml (minimal)">
    ```yaml theme={"dark"}
      config:
        HOOP_KEY: '<agent-key-dsn>'
    ```
  </Accordion>

  <Accordion title="values.yaml (full)">
    ```yaml theme={"dark"}
    # base configuration
    config:
      HOOP_KEY: '<agent-key-dsn>'
      LOG_ENCODING: 'json' # json|console
      LOG_LEVEL: 'info' # debug|info|warn|error
      LOG_GRPC: '0' # 0|1|2

      # Override Presidio configuration propagated from the gateway (optional)
      # MSPRESIDIO_ANALYZER_URL: 'http://user:pass@presidio-analyzer:5001'
      # MSPRESIDIO_ANONYMIZER_URL: 'http://user:pass@presidio-anonymizer:5002'
      # DLP_MODE: 'best-effort' # best-effort|strict

    # image default configuration
    image:
      repository: hoophq/hoopdev
      pullPolicy: Always
      tag: latest

    # define extra secret configuration to load as environment variables
    extraSecret: {}

    # -- Deployment strategy
    deploymentStrategy:
      type: Recreate

    # -- CPU/Memory resource requests/limits
    resources: {}
    #   limits:
    #     cpu: 1024m
    #     memory: 1Gi
    #   requests:
    #     cpu: 1024m
    #     memory: 1Gi

    # -- Node labels for pod assignment
    nodeSelector: {}

    # -- Toleration labels for pod assignment
    tolerations: []

    # -- Affinity settings for pod assignment
    affinity: {}

    ```
  </Accordion>
</AccordionGroup>

### Helm

Make sure you have helm installed in your machine. Check [Helm installation page](https://helm.sh/docs/intro/install/)

```bash theme={"dark"}
VERSION=$(curl -s https://releases.hoop.dev/release/latest.txt)
helm upgrade --install hoopagent \
	oci://ghcr.io/hoophq/helm-charts/hoopagent-chart --version $VERSION \
	--set "config.HOOP_KEY=<AUTH-KEY>"
```

### Using Helm Manifests

```bash theme={"dark"}
VERSION=$(curl -s https://releases.hoop.dev/release/latest.txt)
helm template hoopagent \
  oci://ghcr.io/hoophq/helm-charts/hoopagent-chart --version $VERSION \
  --set 'config.HOOP_KEY=<AUTH-KEY>' \
  --set 'image.tag=1.36.16' \
  --set 'extraSecret=AWS_REGION=us-east-1'
```

<Warning>
  Starting from version **1.21.9**, there is only one way to configure the agent key, which is by using the `config.HOOP_KEY` configuration. This requires creating a key in a DSN format in the API. To use legacy options, use the Helm chart version **1.21.4**.
</Warning>

<Note>
  Need short-lived agent credentials instead of a static `HOOP_KEY`?
  The same `hoopagent-chart` also supports SPIFFE JWT-SVID
  authentication with a `spiffe-helper` sidecar — see
  [SPIFFE Agent Identity → Deploying with the official Helm chart](/setup/deployment/spiffe#deploying-the-spiffe-agent-with-the-official-helm-chart)
  for the step-by-step guide.
</Note>

### Standalone Deployment

<AccordionGroup>
  <Accordion title="deployment.yaml">
    ```yaml theme={"dark"}
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: hoopagent
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: hoopagent
      template:
        metadata:
          labels:
            app: hoopagent
        spec:
          containers:
          - name: hoopagent
            image: hoophq/hoopdev
            env:
            - name: HOOP_KEY
              value: '<AUTH-KEY>'
    ```
  </Accordion>
</AccordionGroup>

### Sidecar Container

<AccordionGroup>
  <Accordion title="deployment.yaml">
    ```yaml theme={"dark"}
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: myapp
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: myapp
        template:
          metadata:
            labels:
              app: myapp
          spec:
            containers:
            - name: myapp
              image: myapp
              ports:
              - containerPort: 8000
                name: http
                protocol: TCP
            - name: hoopagent
              image: hoophq/hoopdev
              env:
              - name: HOOP_KEY
                value: '<AUTH-KEY>'
    ```
  </Accordion>
</AccordionGroup>

## Gateway Chart Configuration

Check the [environment variables section](/setup/configuration/env-vars) for more information about the configuration of the section `config`.
Example:

```yaml theme={"dark"}
config:
  POSTGRES_DB_URI: 'postgres://user:pwd@host:port/db'
  (...)
```

### TLS Configuration

Starting with version **1.45+**,we are transitioning to expose native protocols directly on the Gateway, eliminating the need to forward ports locally through the Hoop Command Line.

This new approach requires the Gateway to terminate TLS connections, ensuring secure protocol negotiation and protecting data in transit.

* To deploy the gateway with a valid certificate, make to define the environment variables `TLS_KEY` and `TLS_CERT`.

<Note>
  The certificate file may contain the **Root** and **Intermediate CA's** as well.
  Make sure to include them in the proper order, the example below show how the certificates must be generated:

  ```
  <SERVER-CERT>
  <INTERMEDIATE-CA>
  <ROOT_CA>
  ```
</Note>

<Tabs>
  <Tab title="Base64 Encoded Configuration">
    ```yaml theme={"dark"}
    config:
      TLS_KEY: 'base64://<pem-encoded-private-key>'
      TLS_CERT: 'base64://<pem-encoded-full-certificate>'
    ```

    Example of how to encode the files:

    <Note>
      Use the output of each value to configure the attributes above.
    </Note>

    ```sh theme={"dark"}
    echo "base64://$(cat /tmp/tls/server.key |base64)
    echo "base64://$(cat /tmp/tls/server.crt |base64)
    ```
  </Tab>

  <Tab title="Path Based Configuration">
    ```yaml theme={"dark"}
    config:
      TLS_KEY: 'file:///path/to/server.key'
      TLS_CERT: 'file:///path/to/server.crt'
    ```
  </Tab>
</Tabs>

### Authentication

<Tabs>
  <Tab title="Local Authentication">
    Local Authentication manages users and passwords locally and sign JWT access tokens to users.

    ```yaml theme={"dark"}
    config:
      POSTGRES_DB_URI: 'postgres://<user>:<pwd>@<db-host>:<port>/<dbname>'
      API_URL: 'https://hoopdev.yourdomain.tld'
    ```
  </Tab>

  <Tab title="Oauth2/OIDC Authentication">
    The Oauth2/OIDC authentication integrates with any identity provider that support these protocols. The users are managed on the identity provider.

    ```yaml theme={"dark"}
    config:
      POSTGRES_DB_URI: 'postgres://<user>:<pwd>@<db-host>:<port>/<dbname>'
      API_URL: 'https://hoopdev.yourdomain.tld'
      IDP_ISSUER: 'https://idp-issuer-url'
      IDP_CLIENT_ID: 'client-id'
      IDP_CLIENT_SECRET: 'client-secret'
      IDP_AUDIENCE: ''
      IDP_CUSTOM_SCOPES: ''
      IDP_GROUPS_CLAIM: ''
    ```

    <Warning>
      The authentication configuration is now handled via Webapp or defined via API directly,
      the environment variable with the prefix `IDP_` are deprecated and will be removed in upcoming releases.
    </Warning>
  </Tab>
</Tabs>

### Default Database

The chart allows deploying a Postgres database as part of the installation.

```yaml theme={"dark"}
# -- Enable PostgreSQL
postgres:
  # it default to host mount when enabled
  enabled: false

  # set a storage class name to use a Persistent Volume Claim
  storageClassName: null

  # -- Size of PVC
  size: 10Gi
  # annotations: {}
```

<Tip>
  It creates a default Service resource named `hoopgateway-pg`.
  This service name could be used in the `POSTGRES_DB_URI` configuration.
</Tip>

### Persistence

We recommend using SSD for large deployments, it will help speed the I/O when handling many concurrent requests.
The following example shows how to enable a 50GB persistent volume when using AWS/EKS.

```yaml theme={"dark"}
persistence:
  # -- Use persistent volume for write ahead log sessions
  enabled: true
  storageClassName: gp2

  # -- Size of persistent volume claim
  size: 50Gi
```

### Ingress Configuration

This section covers the ingress configuration. The gateway requires exposing the ports **HTTP/8009** and **HTTP2/8010.**
The ingress configuration establishes these two differing configurations based on the [ingress controller](https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/) in use.

<Tabs>
  <Tab title="AWS ALB">
    AWS Load Balancer Controller is a controller to help manage Elastic Load Balancers for a Kubernetes cluster.

    <Steps>
      <Step title="Deploy the AWS Load Balancer Controller">
        * [https://kubernetes-sigs.github.io/aws-load-balancer-controller/latest/deploy/installation/](https://kubernetes-sigs.github.io/aws-load-balancer-controller/latest/deploy/installation/)
      </Step>

      <Step title="Ingress Configuration">
        ```yaml theme={"dark"}
        # HTTP/8009 - API / WebApp
        ingressApi:
          enabled: true
          # the public DNS name
          host: 'hoopgateway.yourdomain.tld'
          # the ingress class, in this case alb
          ingressClassName: 'alb'
          annotations:
            # uses the ACM service to use a valid public certificate issued by AWS
            alb.ingress.kubernetes.io/certificate-arn: 'arn:aws:acm:...'
            # the group name allows resuing the same lb for both protocols (HTTP/gRPC)
            alb.ingress.kubernetes.io/group.name: 'hoopdev'
            alb.ingress.kubernetes.io/healthcheck-path: '/'
            alb.ingress.kubernetes.io/healthcheck-protocol: 'HTTP'
            alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS": 443}]'
            alb.ingress.kubernetes.io/scheme: 'internet-facing'
            alb.ingress.kubernetes.io/ssl-redirect: '443'
            alb.ingress.kubernetes.io/target-type: 'ip'

        # HTTP/8010 - gRPC Service
        ingressGrpc:
          enabled: true
          # the public DNS name
          host: 'hoopdev.yourdomain.tld'
          # the ingress class, in this case alb
          ingressClassName: 'alb'
          annotations:
            # configures the type of the protocol
            alb.ingress.kubernetes.io/backend-protocol-version: 'GRPC'
            # the certificate could be reused for the same protocol
            alb.ingress.kubernetes.io/certificate-arn: 'arn:aws:acm:...'
            # the group name allows resuing the same lb for both protocols (HTTP/gRPC)
            alb.ingress.kubernetes.io/group.name: 'hoopdev'
            alb.ingress.kubernetes.io/healthcheck-path: '/'
            alb.ingress.kubernetes.io/healthcheck-protocol: 'HTTP'
            alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS": 8443}]'
            alb.ingress.kubernetes.io/scheme: 'internet-facing'
            alb.ingress.kubernetes.io/target-type: 'ip'
        ```
      </Step>
    </Steps>
  </Tab>

  <Tab title="Nginx Ingress Controller">
    The Nginx Ingress Controller is an Ingress controller for Kubernetes using NGINX as a reverse proxy and load balancer.

    <Steps>
      <Step title="Deploy Nginx Ingress Controller">
        * [https://kubernetes.github.io/ingress-nginx/deploy/](https://kubernetes.github.io/ingress-nginx/deploy/)
      </Step>

      <Step title="Ingress Configuration">
        * TLS Termination on Nginx

        ```yaml theme={"dark"}
        ingressApi:
          enabled: true
          host: hoopgateway.yourdomain.tld
          ingressClassName: 'nginx'
          tls:
          - hosts:
              - hoopgateway.yourdomain.tld
            secretName: hoopserver-tls

        ingressGrpc:
          enabled: true
          host: "hoop-grpc.yourdomain.tld"
          ingressClassName: 'nginx'
          annotations:
            nginx.ingress.kubernetes.io/backend-protocol: GRPC
            nginx.ingress.kubernetes.io/proxy-body-size: '0'
          tls:
          - hosts:
              - hoop-grpc.yourdomain.tld
            secretName: hoopserver-tls
        ```

        <Note>
          This setup requires deploying a network load balancer (Layer 4) in your cloud provider.
        </Note>
      </Step>
    </Steps>
  </Tab>

  <Tab title="GCP Classic ALB">
    The external Application Load Balancer is a proxy-based Layer 7 load balancer that enables you to run and scale your services behind a single external IP address.
    [See the Architecture Overview](https://cloud.google.com/load-balancing/docs/https).

    <Note>
      The Classic ALB doesn't support establishing HTTP/2 connections with the Hoop Gateway without TLS.
      To accommodate this requirement, TLS certificates must be configured both on the ALB and within the Hoop Gateway
      to establish secure communication between these components.
    </Note>

    <Steps>
      <Step title="Deploy GKE Cluster">
        * Deploy GKE Cluster by following the [GKE Quick Start guide](https://cloud.google.com/kubernetes-engine/docs/quickstarts/create-cluster)
        * Obtain access to [your cluster via kubectl](https://cloud.google.com/kubernetes-engine/docs/how-to/cluster-access-for-kubectl)
      </Step>

      <Step title="Export your domain name">
        ```sh theme={"dark"}
        export DOMAIN_HOSTNAME=hoopgateway.yourdomain.tld
        ```
      </Step>

      <Step title="Setup Certificates">
        <AccordionGroup>
          <Accordion title="Generate self signed certificates">
            <Note>
              This step is not necessary if you already have certificates issued by a known entity.
            </Note>

            * Create the CA private key

            ```sh theme={"dark"}
            mkdir -p /tmp/hoopdemo && cd /tmp/hoopdemo
            openssl genrsa -aes256 -out ca.key 2048
            ```

            * Create the Root Certificate Authority (CA)

            ```sh theme={"dark"}
            openssl req -x509 -new -nodes -key ca.key -sha256 -days 1826 -out ca.crt -subj '/CN=Hoop Root CA'
            ```

            * Creating the Certificate Signing Request (CSR)

            ```sh theme={"dark"}
            openssl req -new -nodes -out server.csr -newkey rsa:2048 -keyout server.key -subj '/CN=HoopGateway'
            ```

            * Sign the Certificate

            ```sh theme={"dark"}
            # create a v3 ext file for SAN properties
            cat > server.v3.ext << EOF
            authorityKeyIdentifier=keyid,issuer
            basicConstraints=CA:FALSE
            keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
            subjectAltName = @alt_names
            [alt_names]
            DNS.1 = $DOMAIN_HOSTNAME
            DNS.2 = grpc-$DOMAIN_HOSTNAME
            IP.1 = 127.0.0.1
            EOF
            ```

            ```sh theme={"dark"}
            openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
                -CAcreateserial -out server.crt -days 730 -sha256 -extfile server.v3.ext
            ```

            <Note>
              Make sure to install the Root Certificate Authority in your browser/system
              to be able to visit the Web App. The browser won't allow access due to HSTS policy.

              This step is not necessary when using certificates issued by a known entity.
            </Note>
          </Accordion>
        </AccordionGroup>

        * Export the certificates for setting up the helm chart

        ```sh theme={"dark"}
        export TLS_CA="base64://$(cat ca.crt | base64)"
        export TLS_KEY="base64://$(cat server.key | base64)"
        export TLS_CERT="base64://$(cat server.crt | base64)"
        ```

        <Note>
          The format `base64://<inline-certificate-content>` allows configuring inline TLS to Hoop Gateway.
        </Note>

        * Upload them into GCP

        ```sh theme={"dark"}
        gcloud compute ssl-certificates create hoopserver \
          --certificate=server.crt \
          --private-key=server.key
        ```

        <Tip>
          We recommend using a valid certificates for production workloads.
        </Tip>
      </Step>

      <Step title="Configure DNS and create static Global IP Addresses">
        * Create the ip addresses of the load balancer

        ```sh theme={"dark"}
        gcloud compute addresses create hoopgateway-grpc --global
        gcloud compute addresses create hoopgateway-http --global
        ```

        * Associate the ip address to the proper domains in your DNS provider

        | Public DNS              | IP Address                      |
        | ----------------------- | ------------------------------- |
        | `$DOMAIN_HOSTNAME`      | `<hoopgateway-http-ip-address>` |
        | `grpc-$DOMAIN_HOSTNAME` | `<hoopgateway-grpc-ip-address>` |
      </Step>

      <Step title="Deploy the Hoop Gateway">
        * Create a namespace

        ```sh theme={"dark"}
        kubectl create ns hoopdemo
        ```

        <AccordionGroup>
          <Accordion title="Deploy a Postgres Server">
            * Generate Specification

            ```sh theme={"dark"}
            cat - > /tmp/hoopdemo/postgres-spec.yaml <<EOF
            apiVersion: apps/v1
            kind: Deployment
            metadata:
              name: postgres
            spec:
              replicas: 1
              selector:
                matchLabels:
                  app: postgres
              strategy:
                type: Recreate
              template:
                metadata:
                  labels:
                    app: postgres
                spec:
                  containers:
                  - env:
                    - name: POSTGRES_USER
                      value: root
                    - name: POSTGRES_PASSWORD
                      value: 1a2b3c4d
                    - name: POSTGRES_DB
                      value: hoopdb
                    image: postgres
                    name: postgres
                    ports:
                    - containerPort: 5432
                      name: pg
                      protocol: TCP
            ---
            apiVersion: v1
            kind: Service
            metadata:
              name: postgres
            spec:
              ports:
              - name: postgres
                port: 5432
                protocol: TCP
                targetPort: 5432
              selector:
                app: postgres
            EOF
            ```

            * Deploy it

            ```sh theme={"dark"}
            kubectl apply -n hoopdemo -f /tmp/hoopdemo/postgres-spec.yaml
            ```
          </Accordion>
        </AccordionGroup>

        <AccordionGroup>
          <Accordion title="Deploy the Hoop Gateway">
            * Generate Helm values.yaml

            ```sh theme={"dark"}
            cat - > /tmp/hoopdemo/values.yaml <<EOF
            config:
              POSTGRES_DB_URI: 'postgres://root:1a2b3c4d@postgres:5432/hoopdb?sslmode=disable'
              API_URL: "https://$DOMAIN_HOSTNAME"
              GRPC_URL: "grpcs://grpc-$DOMAIN_HOSTNAME:443"

            defaultAgent:
              enabled: true
              grpcHost: grpc-$DOMAIN_HOSTNAME:443

            mainService:
              annotations:
                beta.cloud.google.com/backend-config: '{"ports": {"http": "hoopgateway-http", "grpc": "hoopgateway-grpc"}}'
                cloud.google.com/app-protocols: '{"http":"HTTPS", "grpc":"HTTP2"}'
              httpBackendConfig:
                healthCheckType: HTTPS
              grpcBackendConfig:
                healthCheckType: HTTPS
                # it avoids clients being dropped for being idle
                timeoutSec: 259200

            ingressApi:
              enabled: true
              host: "$DOMAIN_HOSTNAME"
              annotations:
                kubernetes.io/ingress.class: 'gce'
                ingress.gcp.kubernetes.io/pre-shared-cert: 'hoopserver'
                kubernetes.io/ingress.global-static-ip-name: 'hoopgateway-http'

            ingressGrpc:
              enabled: true
              host: "grpc-$DOMAIN_HOSTNAME"
              annotations:
                kubernetes.io/ingress.class: 'gce'
                ingress.gcp.kubernetes.io/pre-shared-cert: 'hoopserver'
                kubernetes.io/ingress.global-static-ip-name: 'hoopgateway-grpc'
            EOF
            ```

            * Deploy it

            ```sh theme={"dark"}
            helm upgrade --install hoop oci://ghcr.io/hoophq/helm-charts/hoop-chart -f values.yaml --namespace hoopdemo \
              --set config.TLS_CA=$TLS_CA \
              --set config.TLS_KEY=$TLS_KEY \
              --set config.TLS_CERT=$TLS_CERT
            ```
          </Accordion>
        </AccordionGroup>
      </Step>

      <Step title="Access It">
        * Wait for all resources to be provisioned, the command below will show any pending operations on GCP

        ```sh theme={"dark"}
        gcloud compute operations list |grep -v DONE
        ```

        * Check if the ip addresses are displayed when listing the ingresses

        ```sh theme={"dark"}
        kubectl get ing -n hoopdemo
        ```

        ```
        NAME               CLASS    HOSTS                   ADDRESS
        hoopgateway-grpc   <none>   grpc-$DOMAIN_HOSTNAME   XX.X.XX.XXX
        hoopgateway-web    <none>   $DOMAIN_HOSTNAME        XX.XXX.XXX.X
        ```

        * Navigate to the Web Application public URL at `https://$DOMAIN_HOSTNAME/login`
      </Step>
    </Steps>
  </Tab>
</Tabs>

### Proxy Protocol Services

Starting from version **1.45+**, the protocols are served directly on the Gateway.
These ports must be exposed to your network via a Network Load Balancer.

**Configuration Example:**

```yaml theme={"dark"}
proxyService:
  enabled: true
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: nlb
    service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
  ports:
  - name: api
    port: 443
    targetPort: 8009
  - name: grpcs
    port: 8443
    targetPort: 8010
  - name: pgproxy
    port: 15432
    targetPort: 15432
  - name: sshproxy
    port: 12222
    targetPort: 12222
  - name: rdpproxy
    port: 13389
    targetPort: 13389
  - name: httpproxy
    port: 18888
    targetPort: 18888
```

<Note>
  This setup requires configuring TLS directly on the gateway.
</Note>

### Computing Resources

The helm-chart defaults to 1vCPU and 1GB, which is suitable for evaluation purposes only.
For production setups, we recommend allocating at least 8GB/4vCPU to the gateway process.

```yaml theme={"dark"}
resources:
  gw:
    limits:
      cpu: 4096m
      memory: 8Gi
    requests:
      cpu: 4096m
      memory: 8Gi
```

### Image Configuration

By default, the latest version of all images is used. If you want to use a specific image or pin the versions, use the `image` attribute section.

```yaml theme={"dark"}
image:
  gw:
    repository: hoophq/hoop
    pullPolicy: Always
    tag: latest
```

### Default Agent Sidecar

Adding this section will deploy a default agent as a sidecar container.

```yaml theme={"dark"}
defaultAgent:
  enabled: true
  imageRepository: 'hoophq/hoopdev'
  imageTag: latest
  imagePullPolicy: Always
  grpcHost: 127.0.0.1:8009
```

<Note>
  The `grpcHost` allows configuring the host to connect when starting the agent.
  In case the gateway has TLS configured (`TLS_CA` env set), the host must match the certificate SAN.
</Note>

### Data Masking Configuration

To enable the Data Masking feature, you need to configure the `dataMasking` section in your `values.yaml` file.
It will deploy the [Microsoft Presidio](https://github.com/microsoft/presidio) on the same namespace as the Hoop Gateway.

```yaml theme={"dark"}
dataMasking:
  enabled: true
  # https://github.com/microsoft/presidio/releases
  version: latest
  # best-effort | strict
  mode: best-effort

  analyzer:
    replicas: 2
    resources:
      requests:
        cpu: 2048m
        memory: 1024Mi
      limits:
        cpu: 2500m
        memory: 2048Mi
```

<Note>
  When the `dataMasking` attribute is enabled, it takes control over the following configurations:

  * DLP\_MODE
  * DLP\_PROVIDER
  * MSPRESIDIO\_ANALYZER\_URL
  * MSPRESIDIO\_ANONYMIZER\_URL
  * GOOGLE\_APPLICATION\_CREDENTIALS\_JSON

  If you need more control over the deployment, we recommend using a standalone helm chart of Presidio.
  See more details above in the [Presidio Deployment](/setup/deployment/presidio) section.
</Note>

<Tip>
  This attribute is available starting from version **1.37.16+** of the Helm chart.
</Tip>

### Node Selector

This configuration describes a pod that has a node selector, `disktype: ssd`. This means that the pod will get scheduled on a node that has a `disktype=ssd` label.

See [this documentation](https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes/) for more information.

```yaml theme={"dark"}
# -- Node labels for pod assignment
nodeSelector:
  disktype: ssd
```

### Tolerations

See this article explaining how to configure [tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/)

```yaml theme={"dark"}
# -- Toleration labels for pod assignment
tolerations:
- effect: NoExecute
  key: spot
  value: "true"
- effect: NoSchedule
  key: spot
  value: "true"
```

### Node Affinity

See [this article](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) explaining how to configure affinity and anti-affinity rules

```yaml theme={"dark"}
# -- Affinity settings for pod assignment
affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: topology.kubernetes.io/zone
          operator: In
          values:
          - antarctica-east1
          - antarctica-west1
    preferredDuringSchedulingIgnoredDuringExecution:
    - weight: 1
      preference:
        matchExpressions:
        - key: another-node-label-key
          operator: In
          values:
          - another-node-label-value
```

## Presidio Deployment

The Data Masking feature uses Microsoft Presidio. We provide a Helm chart that gives more control over the deployment.

```sh theme={"dark"}
helm upgrade --install presidio \
  oci://ghcr.io/hoophq/helm-charts/presidio-chart --version v0.1.0 \
  -f values.yaml
```

We recommend starting with at least 2 replicas to handle concurrent requests more efficiently:

```yaml theme={"dark"}
analyzer:
  replicas: 2

  resources:
    requests:
      cpu: 2048m
      memory: 1024Mi
    limits:
      cpu: 2500m
      memory: 2048Mi
```

<Note>
  It will guarantee at least four concurrent requests when performing model inference.
  If you want the workload to be burstable, decrease the CPU requests to 1024m.
  For maximum performance, ensure CPU requests are always set to 2 vCPUs and enable the Horizontal Pod Autoscaler.
</Note>

These services must be respectively configured in the Gateway with the following environment variables:

```conf theme={"dark"}
DLP_PROVIDER=mspresidio
MSPRESIDIO_ANALYZER_URL=http://presidio-envoy-lb:3010
MSPRESIDIO_ANONYMIZER_URL=http://presidio-envoy-lb:3010
```

For more information about Presidio Deployment, see [this section](/setup/deployment/presidio).

## Generating Manifests

If you prefer using manifests over Helm, we recommend this approach. It allows you to track any modifications to the chart whenever a new version appears. You can apply a diff to your versioned files to identify what has been altered.

```bash theme={"dark"}
VERSION=$(curl -s https://releases.hoop.dev/release/latest.txt)
helm template hoop \
  oci://ghcr.io/hoophq/helm-charts/hoop-chart --version $VERSION \
  -f values.yaml
```
