# Define preflight checks

This topic describes how to define custom preflight checks for your application. For more information about preflight checks, see [About Preflight Checks and Support Bundles](/vendor/preflight-support-bundle-about).

:::note
These examples use `troubleshoot.sh/v1beta2` (supported by KOTS, kURL, and Embedded Cluster v2). Embedded Cluster v3 requires preflight specs to use `troubleshoot.sh/v1beta3`, packaged as a release-level file and rendered with Helm template syntax (not `repl{{ }}`). `troubleshoot.sh/v1beta3` is not supported by KOTS or Embedded Cluster v2. This change does not affect support bundle specs, which do not support `v1beta3` on any installer. See [Preflight specs must use v1beta3](/embedded-cluster/v3/embedded-v3-migrate#preflight-specs-must-use-v1beta3).
:::

## Add a preflight spec to a release

You can define custom preflight checks for your application by adding a preflight spec to your Helm chart templates.

To add a preflight spec to a release for your application:

1. In your Helm chart `templates` directory, create a YAML file. Name the file `preflights.yaml` or similar.

1. In the YAML file, add the following to create a Kubernetes Secret with the label `troubleshoot.sh/kind: preflight`:

   ```yaml
   # templates/preflight.yaml
    apiVersion: v1
    kind: Secret
    metadata:
      # the troubleshoot.sh/kind: preflight label is required
      labels:
        troubleshoot.sh/kind: preflight
      name: "{{ .Release.Name }}-preflight-config"
   ```

1. In the Secret, add a `stringData` field with a key named `preflight.yaml`. This ensures the `preflight` binary can use this Secret when it runs from the CLI.

    ```yaml
    # templates/preflight.yaml
    apiVersion: v1
    kind: Secret
    metadata:
      labels:
        troubleshoot.sh/kind: preflight
      name: "{{ .Release.Name }}-preflight-config"
    stringData:
      # add a preflight.yaml key under stringData
      preflight.yaml: |
        apiVersion: troubleshoot.sh/v1beta2
        kind: Preflight
        metadata:
          name: preflights
        spec:
          collectors: []
          analyzers: []
    ```

1. Update `spec.collectors` and `spec.analyzers` with any collectors and analyzers that you want to use in the preflight checks for your application. For common examples, see [Examples of preflight specs](/vendor/preflight-examples).

    For the complete list of available collectors and analyzers, see the [Collect](https://troubleshoot.sh/docs/collect/all/) and [Analyze](https://troubleshoot.sh/docs/analyze/) sections in the Troubleshoot documentation.

    :::note
    Troubleshoot automatically includes the [clusterInfo](https://troubleshoot.sh/docs/collect/cluster-info/) and [clusterResources](https://troubleshoot.sh/docs/collect/cluster-resources/) collectors to gather information about the cluster and cluster resources. You do not need to manually include the `clusterInfo` or `clusterResources` collectors in the specification.
    
    To use only the `clusterInfo` and `clusterResources` collectors, delete the `spec.collectors` key from the preflight spec.
    :::

1. Test the preflight checks by running them in a cluster using the preflight plugin and Helm CLI. For information about how to install the preflight plugin and run preflight checks, see [Run preflight checks for Helm installations](/vendor/preflight-running).

1. From the root directory of your Helm chart, package the chart into a `.tgz` archive:

   ```bash
   helm package --dependency-update .
   ```

1. Add the chart archive to a new release. Promote the release to an internal development channel, and install the release in a development environment to test your changes.

## Use a credential in a preflight check {#credentials}

The `mysql`, `postgres`, `mssql`, and `redis` collectors accept credentials only as a literal string in the `uri` field. Helm template functions and KOTS config options do not protect the credential. These functions and config options resolve to the live credential when the spec is rendered and deployed to the cluster, where a support bundle can collect the credential in plain text. The built-in redactors do not reliably mask these values.

To check a credentialed resource without exposing the credential, use a `runPod` collector and pass the credential to the Pod with `env.valueFrom.secretKeyRef`. The kubelet resolves the credential when the Pod starts, so the credential never appears in the spec.

Replicated recommends that you design these preflight checks around a pre-existing Secret. The Secret must already exist in the cluster when preflight checks run. Reference the Secret by name in the spec, and document the name and the keys that your preflight checks expect, such as `password`.

A Secret created by your application's own chart does not work, because application charts install after preflight checks run. This applies even when the Secret is annotated with `helm.sh/hook: pre-install`, because Helm hooks run during the installation of the chart that defines them.

For Embedded Cluster v3 installations, you can create the Secret in a Helm chart extension. Extensions install before application preflight checks, and the `values` field of a Helm extension supports Replicated template functions. This means that you can template a password that the user provides on the config screen into the value of the Secret. For more information, see [Deployment pipeline](/embedded-cluster/v3/embedded-overview#deployment-pipeline) and [extensions](/embedded-cluster/v3/embedded-config#extensions).

Embedded Cluster v2 does not support template functions in the Embedded Cluster Config, so you cannot pass a user-provided password to a Helm extension. For Embedded Cluster v2, do not check the credential in a preflight check. Use a support bundle after the installation completes instead. For more information, see [Pass a credential to the pod](/vendor/support-bundle-examples#run-pod-credentials) in _Example support bundle specs_.

More generally, if the credential is not available before preflight checks run, validate it in your application instead of in a preflight check.

The following example checks a PostgreSQL connection using a password from a pre-existing Secret named `my-app-postgres`.

:::note
The `runPod` collector requires `pods/create` RBAC. The collector saves everything that the Pod writes to stdout into the preflight output, so make sure that the command does not print the credential.
:::

```yaml
apiVersion: v1
kind: Secret
metadata:
  name: example
  labels:
    troubleshoot.sh/kind: support-bundle
stringData:
  support-bundle-spec: |-
    apiVersion: troubleshoot.sh/v1beta2
    kind: SupportBundle
    metadata:
      name: example
    spec:
      collectors:
        - runPod:
            collectorName: postgres-connection
            # the Pod must run in the namespace where the Secret exists
            namespace: '{{ .Release.Namespace }}'
            timeout: 60s
            podSpec:
              restartPolicy: Never
              containers:
              - name: postgres-connection
                image: postgres:16-alpine
                command: ["/bin/sh", "-c"]
                # the command must not print the credential
                args:
                  - psql --quiet --no-password --command 'SELECT 1;' && echo "connection succeeded"
                env:
                - name: PGHOST
                  value: my-app-postgres
                - name: PGUSER
                  value: my-app
                - name: PGDATABASE
                  value: my-app
                # the spec contains only the Secret name and key, not the password
                - name: PGPASSWORD
                  valueFrom:
                    secretKeyRef:
                      name: my-app-postgres
                      key: password
      analyzers:
        - textAnalyze:
            checkName: PostgreSQL connection
            fileName: /postgres-connection.log
            regex: 'connection succeeded'
            outcomes:
              - fail:
                  message: Could not connect to the PostgreSQL database.
              - pass:
                  message: Connected to the PostgreSQL database.
```

## (Optional) Block installation with required (strict) preflights {#strict}

For applications installed with a Replicated installer (Embedded Cluster, KOTS, kURL), you can set any preflight analyzer to `strict: true`. When you set `strict: true`, any `fail` outcomes for the analyzer block the deployment of the release.

:::note
Troubleshoot ignores strict preflight analyzers if the `exclude` property is also included and evaluates to `true`. See [exclude](https://troubleshoot.sh/docs/analyze/#exclude) in the Troubleshoot documentation.
:::

The following image shows how a `fail` outcome appears when you set `strict: true` on an analyzer, which prevents installation from continuing if the preflight check fails:

![Preflight checks in Admin Console showing fail message with strict mode](/images/preflight-mysql-fail-strict.png)

[View a larger version of this image](/images/preflight-mysql-fail-strict.png)

## Define preflights for non-Helm applications or KOTS v1.100.3 and earlier

For non-Helm applications or installations with KOTS v1.100.3 and earlier, add the Preflight custom resource to a YAML file at the root level of your release:

```yaml
# preflights.yaml

apiVersion: troubleshoot.sh/v1beta2
kind: Preflight
metadata:
  name: preflights
spec:
  collectors: []
  analyzers: []
```

Customize the collectors and analyzers as desired.