# Replicated quick start

This topic provides a quick start workflow to help new users learn about the Replicated Platform. Complete this quick start before you onboard your application to the platform.

## Introduction

This quick start demonstrates how to create, install, and update releases for a sample Helm chart in the Replicated Platform. You will repeat these same basic steps to create and test releases throughout the onboarding process to integrate Replicated features with your own application.

The goals of this quick start are to introduce new Replicated users to the following common tasks for the purpose of preparing to onboard to the Replicated Platform:

* Adding the Replicated SDK to a Helm chart as a dependency

* Working with _applications_, _channels_, _releases_, and _customers_ in the Replicated Platform

* Working with the Replicated CLI

* Installing and updating applications on a VM with Replicated Embedded Cluster through the Replicated Admin Console UI.

Replicated recommends that you allow one to two hours to complete this quick start. No prior knowledge of Replicated, Helm, or interacting with the command line is required. However, if you are unfamiliar with these tools, you might allow more time. If you have any questions or need help, don't hesitate to reach out to our team at https://www.replicated.com/contact.

## Set up your environment

Before you begin, do the following to set up your environment:

* Install the Helm CLI, which is the tool for interacting with Helm and managing Helm charts. See [Install Helm](/vendor/environment-setup#install-helm).

* Ensure that you have access to a VM that meets the requirements for Embedded Cluster:

   * **Option 1: Use Compatibility Matrix.** To use Replicated Compatibility Matrix (CMX) to create a VM, do the following before proceeding:
      
      * Request CMX credits. You can request credits by creating a Vendor Portal account and then going to [**Compatibility Matrix > Request more credits**](https://vendor.replicated.com/compatibility-matrix) in the Vendor Portal. For more information about creating an account, see [Create a Vendor Account](vendor-portal-creating-account). For more information about CMX credits, see [Billing and Credits](/vendor/testing-about#billing-and-credits).

          :::note
          If you are new to the Replicated platform, you might be eligible for $100 in free CMX credits. To request your free credits, reach out to our sales team at https://www.replicated.com/contact and note in the comments that you are completing the Replicated Quick Start.
          :::
      
      * Ensure that you have an SSH key in your GitHub account. Then, add your GitHub username to your Vendor Portal [**Account Settings**](https://vendor.replicated.com/account-settings). This will provide SSH access to VMs that you create with CMX. For more information, see [Prerequisite: Set Up SSH](/vendor/testing-vm-create#set-up-ssh) in _Connect to CMX VMs (SSH and File Transfer)_.

      After you complete the prerequisites above, continue to the [Quick Start](#quick-start). You will create the VM with CMX as part of the tutorial.

   * **Option 2: Bring your own VM.** Your VM must meet these requirements:

      * Firewalls must allow HTTP and HTTPS traffic.

      * Linux operating system

      * x86-64 architecture

      * systemd

      * At least 2GB of memory and 2 CPU cores

      * The disk on the host must have a maximum P99 write latency of 10 ms. This supports etcd performance and stability. For more information about the disk write latency requirements for etcd, see [Disks](https://etcd.io/docs/latest/op-guide/hardware/#disks) in _Hardware recommendations_ and [What does the etcd warning “failed to send out heartbeat on time” mean?](https://etcd.io/docs/latest/faq/) in the etcd documentation.

      * The user performing the installation must have root access to the machine, such as with `sudo`.

      * The data directory used by Embedded Cluster must have 40Gi or more of total space and be less than 80% full. By default, the data directory is `/var/lib/embedded-cluster`. The directory can be changed by passing the `--data-dir` flag with the Embedded Cluster `install` command. For more information, see [install](/reference/embedded-cluster-install).

         Note that in addition to the primary data directory, Embedded Cluster creates directories and files in the following locations:

            - `/etc/cni`
            - `/etc/k0s`
            - `/opt/cni`
            - `/opt/containerd`
            - `/run/calico`
            - `/run/containerd`
            - `/run/k0s`
            - `/sys/fs/cgroup/kubepods`
            - `/sys/fs/cgroup/system.slice/containerd.service`
            - `/sys/fs/cgroup/system.slice/k0scontroller.service`
            - `/usr/libexec/k0s`
            - `/var/lib/calico`
            - `/var/lib/cni`
            - `/var/lib/containers`
            - `/var/lib/kubelet`
            - `/var/log/calico`
            - `/var/log/containers`
            - `/var/log/embedded-cluster`
            - `/var/log/pods`
            - `/usr/local/bin/k0s`

      * (Online installations only) Access to replicated.app and proxy.replicated.com or your custom domain for each

      * Embedded Cluster is based on k0s, so all k0s system requirements and external runtime dependencies apply. See [System requirements](https://docs.k0sproject.io/stable/system-requirements/) and [External runtime dependencies](https://docs.k0sproject.io/stable/external-runtime-deps/) in the k0s documentation.

   For more information, see [Set Up Development Environments for Testing](/vendor/environment-setup#dev).   

## Quick start

1. If you have not done so already, create an account in the Vendor Portal. You can either create a new team or join an existing team. For more information, see [Create a Vendor Account](vendor-portal-creating-account).

1. Create an application using the Replicated CLI:

   1. On your local machine, install the Replicated CLI:

      * MacOS

        ```bash
        brew install replicatedhq/replicated/cli
        ```
      * Linux / Windows Subsystem for Linux (WSL)

        ```bash
        version=$(curl -s https://api.github.com/repos/replicatedhq/replicated/releases/latest \
          | grep -m1 -Po '"tag_name":\s*"v\K[^"]+')
        curl -Ls \
          "https://github.com/replicatedhq/replicated/releases/download/v${version}/replicated_${version}_linux_amd64.tar.gz" \
          -o replicated.tar.gz
        tar xf replicated.tar.gz replicated && rm replicated.tar.gz
        mv replicated /usr/local/bin/replicated
        ``` 
      For more information and additional installation options, see [Install the Replicated CLI](/reference/replicated-cli-installing).

   1. Authorize the Replicated CLI:

      ```bash
      replicated login
      ```
      In the browser window that opens, follow the prompt to log in to your Vendor Portal account and authorize the CLI.

   1. Create an application named `SlackerNews`:

      ```bash
      replicated app create SlackerNews
      ```

   1. Set the `REPLICATED_APP` environment variable to the application that you created:

      ```bash
      export REPLICATED_APP=APP_SLUG
      ```
      Where `APP_SLUG` is the unique application slug provided in the output of the `app create` command.

      Setting the `REPLICATED_APP` environment variable allows you to interact with the application using the Replicated CLI without needing to use the `--app` flag with every command.

1. Get the sample SlackerNews Helm chart:

   1. Run the following command to download version 1.0.0 of the sample SlackerNews Helm chart to a new `quick-start` directory: 

      ```
      curl -O --create-dirs --output-dir quick-start https://docs.replicated.com/slackernews-1.0.0.tar.gz
      ```
   
   1. Untar the chart:

      ```
      tar -xzf quick-start/slackernews-1.0.0.tar.gz -C quick-start/ && rm quick-start/slackernews-1.0.0.tar.gz
      ```

   1. Change to the `slackernews` chart directory:
      
      ```bash
      cd quick-start/chart/slackernews
      ```

   1. List the files in the `slackernews` directory to view the contents of the Helm chart:
      ```bash
      ls
      ```
      ```bash
      Chart.lock  Chart.yaml  NOTES.txt   README.md   templates   values.yaml
      ```

   1. In the SlackerNews Helm chart `Chart.yaml`, add the Replicated SDK as a dependency:

      ```yaml
      # Chart.yaml
      dependencies:
      - name: replicated
        repository: oci://registry.replicated.com/library
        version: 1.18.2
      ```

      For the latest version information for the Replicated SDK, see the [replicated-sdk repository](https://github.com/replicatedhq/replicated-sdk/releases) in GitHub.

      The Replicated SDK is a Helm chart that provides access to Replicated features and can be installed as a small service alongside your application. For more information, see [About the Replicated SDK](/vendor/replicated-sdk-overview).

   1. Update dependencies and package the Helm chart to a `.tgz` chart archive:

      ```bash
      helm package --dependency-update .
      ```
      Where `--dependency-update` (or `-u`) is an option for the `helm package` command that updates chart dependencies before packaging. For more information, see [Helm Package](https://helm.sh/docs/helm/helm_package/) in the Helm documentation.

      The output of this command is a file named `slackernews-1.0.0.tgz`.

1. Add the `slackernews-1.0.0.tgz` chart archive to a release in the Replicated Platform:

   1. Change to the `quick-start` directory:

      ```bash
      cd ../..
      ```
   
   1. In the `quick-start` directory, create a directory named `manifests`:

      ```
      mkdir manifests
      ```
      You will add the files required to support installation with Embedded Cluster to this subdirectory.

   1. Move the `slackernews-1.0.0.tgz` chart archive that you created to `manifests`:

      ```
      mv chart/slackernews/slackernews-1.0.0.tgz manifests
      ```

   1. In `manifests`, create the following YAML files:
      ```
      cd manifests
      ```
      ```
      touch slackernews.yaml replicated-app.yaml k8s-app.yaml embedded-cluster.yaml config.yaml
      ```
      These manifests will be used by Embedded Cluster to install SlackerNews.

   1. In each of the files that you created, paste the corresponding YAML provided in the tabs below:

      <Tabs>
      <TabItem value="helmchart" label="slackernews.yaml" default>
      <h5>Description</h5>
      <p>The HelmChart custom resource provides instructions to the installer about how to deploy the Helm chart. The <code>name</code> and <code>chartVersion</code> listed in the HelmChart custom resource must match the name and version of a Helm chart archive in the release. This HelmChart resource includes several `values` and conditional `optionalValues` that are supplied to `helm template` during deployment.</p>
      <h5>YAML</h5>
      ```yaml
      apiVersion: kots.io/v1beta2
      kind: HelmChart
      metadata:
        name: slackernews
      spec:
        # chart identifies a matching chart from a .tgz
        chart:
          name: slackernews
          chartVersion: 1.0.0
        namespace: slackernews

        # values are used in the customer environment, as a pre-render step
        # these values will be supplied to helm template
        values:
          postgres:
            enabled: true
            deploy_postgres: repl{{ ConfigOption "deploy_postgres" | ParseBool }}
          slack:
            botToken: repl{{ ConfigOption "slack_bot_token" | quote }}
            userToken: repl{{ ConfigOption "slack_user_token" | quote }}
            clientId: repl{{ ConfigOption "slack_clientid" | quote }}
            clientSecret: repl{{ ConfigOption "slack_clientsecret" | quote }}
          slackernews:
            domain: repl{{ ConfigOption "slackernews_domain" }}
            adminUserEmails: repl{{ ConfigOption "slackernews_admin_user_emails" | quote }}
          admin-console:
            enabled: false
          replicated:
            isEmbeddedCluster: repl{{ eq Distribution "embedded-cluster"}}
            isKOTSManaged: true
            imagePullSecrets:
              - name: '{{repl ImagePullSecretName }}'
          service:
            tls:
              enabled: true
          nginx:
            enabled: true
          images:
            pullSecrets:
              - name: '{{repl ImagePullSecretName }}'

        optionalValues:
          # load images from the local registry in an air-gapped environment
          - when: '{{repl HasLocalRegistry }}'
            recursiveMerge: true
            values:
              replicated:
                isAirgap: true
                image:
                  registry: '{{repl LocalRegistryHost }}'
                  repository: '{{repl LocalRegistryNamespace }}/replicated-sdk-image'
              slackernews:
                registry: '{{repl LocalRegistryHost}}'
                repository: '{{LocalRegistryNamespace}}/slackernews-web'
              nginx:
                registry: '{{repl LocalRegistryHost}}'
                repository: '{{LocalRegistryNamespace}}/nginx'
              postgres:
                registry: '{{repl LocalRegistryHost}}'
                repository: '{{LocalRegistryNamespace}}/postgres'

          # the user wants us to deploy a local Postgres instance
          - when: '{{repl ConfigOptionEquals "deploy_postgres" "1"}}'
            recursiveMerge: true
            values:
              postgres:
                password: '{{repl ConfigOption "postgres_password" }}'

          # the user provided their own Postgres instance
          - when: '{{repl ConfigOptionEquals "deploy_postgres" "0"}}'
            recursiveMerge: true
            values:
              postgres:
                uri: '{{repl ConfigOption "postgres_external_uri" }}'

          # use the user-provided certificates 
          - when: '{{repl ConfigOptionEquals "certificate_source" "upload_existing"}}'
            recursiveMerge: true
            values:
              service:
                tls:
                  enabled: true
                  cert: repl{{ ConfigOptionData "tls_cert" | nindent 14 }}
                  key: repl{{ ConfigOptionData "tls_key" | nindent 14 }}
                  ca: repl{{ ConfigOptionData "tls_ca" | nindent 14 }}
          
          # or generate our own
          - when: '{{repl ConfigOptionEquals "certificate_source" "generate_internal"}}'
            recursiveMerge: true
            values:
              service:
                tls:
                  enabled: true
                  ca: |-
                    {{repl $ca := genCA (LicenseFieldValue "customerName") 365 }}
                    {{repl $ca.Cert | Base64Encode}}
                  cert: |-
                    {{repl $cert := genSignedCert (ConfigOption "slackernews_domain") nil (list (ConfigOption "slackernews_domain")) 365 $ca }}
                    {{repl $cert.Cert | nindent 14 }}
                  key: |-
                    {{repl $cert.Key | nindent 14 }}
             
          # handle different service types
          - when: '{{repl ConfigOptionEquals "service_type" "cluster_ip"}}'
            recursiveMerge: true
            values:
              nginx:
                service:
                  type: ClusterIP

          - when: '{{repl ConfigOptionEquals "service_type" "load_balancer"}}'
            recursiveMerge: true
            values:
              nginx:
                service:
                  type: LoadBalancer

          - when: '{{repl ConfigOptionEquals "service_type" "node_port"}}'
            recursiveMerge: true
            values:
              nginx:
                service:
                  type: NodePort
                  nodePort:
                    port: repl{{ ConfigOption "node_port_port" }}



        # builder values provide a way to render the chart with all images
        # and manifests. this is used in Replicated to create airgap packages
        builder:
          postgres:
            password: this-is-not-used-but-needed-for-builder
            deploy_postgres: true
            enabled: true
      ```
      </TabItem>
      <TabItem value="replicated-app" label="replicated-app.yaml">
      <h5>Description</h5>
      <p>The Replicated Application custom resource enables features in the Replicated Admin Console UI, which is used to configure, install, update, and manage applications. This Application resource provides a name for the application to display in the Admin Console, adds custom <em>status informers</em> that are used to display the status of the deployment in the Admin Console dashboard, and adds a custom application icon.</p>
      <h5>YAML</h5>
      ```yaml
      apiVersion: kots.io/v1beta1
      kind: Application
      metadata:
        name: slackernews
        annotations:
          kots.io/exclude: "true"
      spec:
        title: SlackerNews
        icon: "https://uploads-ssl.webflow.com/6310ad0e6a18aa1620da6ae8/6330e04f42bc6a7ba03b4725_snicon.png"
        statusInformers:
          - slackernews/deployment/slackernews
          - slackernews/deployment/slackernews-nginx
          - '{{repl if ConfigOptionEquals "deploy_postgres" "1"}}slackernews/statefulset/postgres{{repl end}}'
        additionalNamespaces:
          - slackernews
      ```
      </TabItem>
      <TabItem value="k8s-app" label="k8s-app.yaml">
      <h5>Description</h5>
      <p>The Kubernetes SIG Application custom resource supports functionality such as including buttons and links on the Admin Console dashboard. This SIG Application resource adds an <strong>Open SlackerNews</strong> button that opens the application.</p>
      <h5>YAML</h5>
      ```yaml
      apiVersion: app.k8s.io/v1beta1
      kind: Application
      metadata:
        name: "slackernews"
        annotations:
          kots.io/exclude: "true"
        labels:
          app.kubernetes.io/name: "slackernews"
          app.kubernetes.io/version: "0.0.1"
      spec:
        selector:
          matchLabels:
           app.kubernetes.io/name: "slackernews"
        componentKinds:
          - group: core
            kind: Service
          - group: apps
            kind: Deployment
        descriptor:
          version: "1.0.0"
          description: "SlackerNews"
          icons:
            - src: "https://uploads-ssl.webflow.com/6310ad0e6a18aa1620da6ae8/6330e04f42bc6a7ba03b4725_snicon.png"
              type: "image/png"
          type: slackernews
          links:
            - description: 🔗 Open Slackernews
              url: 'https://{{repl ConfigOption "slackernews_domain" }}'
      ```
      </TabItem>
      <TabItem value="ec" label="embedded-cluster.yaml">
      <h5>Description</h5>
      <p>To install your application with Embedded Cluster, an Embedded Cluster Config must be present in the release. At minimum, the Embedded Cluster Config sets the version of Embedded Cluster that will be installed. You can also define several characteristics about the cluster.</p>
      <h5>YAML</h5>
      ```yaml
      apiVersion: embeddedcluster.replicated.com/v1beta1
      kind: Config
      spec:
        version: 2.10.0+k8s-1.33
      ```
      </TabItem>
      <TabItem value="config" label="config.yaml">
      <h5>Description</h5>
      <p>The Config custom resource defines the application configuration fields that are exposed to the user in the Admin Console. These fields can then be mapped to the `values.yaml` file for the Helm chart. This Config resource includes several fields, but you will only use a few of them when installing SlackerNews for the purpose of this tutorial.</p>
      <h5>YAML</h5>
      ```yaml
      apiVersion: kots.io/v1beta1
      kind: Config
      metadata:
        name: slackernews-config
      spec:
        groups:
          - name: slackernews
            title: Application Core
            description: |
              For this section, you can specify some core parameters for how
              Slackernews operates, including the domain where users will access it
              and the user who can administer it.

              Users that you specify under **Admin Users** will be able to access the
              Slackernews adminstrative console at `/admin`, allowing them to manage
              content, users, and settings. Changes will take effect the next time
              they are active in the Slackernews application.
            items:
              - name: slackernews_domain
                title: Ingress Hostname
                help_text: >
                  The domain name at which you'll access SlackerNews. Don't include
                  the `https://` or any path elements.
                type: text
                required: true
                # validation:
                #   regex:
                #     pattern: ^(?!-)[A-Za-z0-9-]{1,63}(?<!-)(\.(?!-)[A-Za-z0-9-]{1,63}(?<!-))*\.[A-Za-z]{2,}$
                #     message: Please enter a valid hostname
              - name: slackernews_admin_user_emails
                title: Admin Users
                type: text
                help_text: >
                  Provide a comma-separated list of email addresses for the users you
                  want to grant admin access to.

          - name: ingress
            title: Application Access
            description: |
              You can customize how you will expose SlackerNews to the internet.
              Note that the domain you use will need to be publicly addressable with certs signed by a public authority
              so it can receive webhooks from Slack.

              Common configurations include:

              - **ClusterIP** Using a Cluster IP and configuring your existing ingress controller to route traffic to SlackerNews
              - **NodePort** Using a NodePort and configuring an existing load balancer to route traffic to SlackerNews
              - **LoadBalancer** Using a LoadBalancer service and letting Kubernetes provision a load balancer for you

              If you're running in a supported cloud provider and want Kubernetes to provision a Load Balancer, use LoadBalancer.
            items:
              - name: service_type
                title: Service Type
                type: select_one
                items:
                  - name: node_port
                    title: NodePort
                  - name: cluster_ip
                    title: ClusterIP
                  - name: load_balancer
                    title: LoadBalancer
                default: node_port
              - name: node_port_port
                title: Node Port
                help_text: >
                    (Optional) - The port to use for the NodePort service type. Leave this blank to have Kubernetes choose a port for you.
                type: text
                default: "443"
                when: repl{{ ConfigOptionEquals "service_type" "node_port" }}

          - name: tls
            title: Certificates
            description: |
              You can secure the Slackernews application with certificates from a trusted certificate authority
              or we can generate them for you. We recommend that you upload your own certificates for production installations.
            items:
              - name: certificate_source
                type: select_one
                title: Certificate Source
                default: generate_internal
                items:
                  - name: generate_internal
                    title: Generate
                  - name: upload_existing
                    title: Upload
              - name: tls_cert
                title: Certificate
                type: file
                when: '{{repl ConfigOptionEquals "certificate_source" "upload_existing"}}'
              - name: tls_key
                title: Private Key
                type: file
                when: '{{repl ConfigOptionEquals "certificate_source" "upload_existing"}}'
              - name: tls_ca
                title: Signing Authority
                type: file
                when: '{{repl ConfigOptionEquals "certificate_source" "upload_existing"}}'

          - name: slack
            title: Slack Settings
            description: |
              If desired, you can preconfigure the slack settings for SlackerNews.
              These are required for logging into SlackerNews and pulling/organizing content from your slack instance.
              If you don't preconfigure these settings, you'll be prompted to configure them when you first access SlackerNews.

              Instructions on how to configure your slack application and collect these values can be found in [the SlackerNews slack documentation](https://docs.slackernews.io/slack/).
            items:
              - name: slack_clientid
                title: Slack Client ID
                type: text
              - name: slack_clientsecret
                title: Slack Client Secret
                type: password
              - name: slack_user_token
                title: User OAuth Token
                type: password
                validation:
                  regex:
                    pattern: ^xoxp-.*$
                    message: Please enter the Slack user token for your instance of Slackernews
              - name: slack_bot_token
                title: Bot User OAuth Token
                type: password
                validation:
                  regex:
                    pattern: ^xoxb-.*$
                    message: Please enter the Slack bot token for your instance of Slackernews

          - name: postgres
            description: >
              This section can be used to configure the postgresql database required by SlackerNews. You
              can either deploy postgresql as part of the installation or provide an external URI to an existing postgresql instance
              that you will use for SlackerNews.
            title: Postgresql
            items:
              - name: deploy_postgres
                type: bool
                title: Deploy Postgresql Database
                default: "1"
              - name: postgres_password
                type: password
                title: Postgresql Password
                required: true
                hidden: true
                when: repl{{ ConfigOptionEquals "deploy_postgres" "1"}}
                value: repl{{ RandomString 40}}
              - name: postgres_external_uri
                type: text
                title: Postgresql URI
                required: true
                when: repl{{ ConfigOptionEquals "deploy_postgres" "0"}}
      ```
      </TabItem>
      </Tabs>

   1. Lint the YAML files in the `manifests` directory:

      ```bash
      replicated release lint --yaml-dir .
      ```
      **Example output:**
      ```bash
      RULE                                TYPE   FILENAME             LINE   MESSAGE
      may-contain-secrets                 info   slackernews.yaml     142    It looks like there might be secrets in this file
      preflight-spec                      warn                               Missing preflight spec
      troubleshoot-spec                   warn                               Missing troubleshoot spec
      nonexistent-status-informer-object  warn   replicated-app.yaml  14     Status informer points to a nonexistent kubernetes object. If this is a Helm resource, this warning can be ignored.
      nonexistent-status-informer-object  warn   replicated-app.yaml  15     Status informer points to a nonexistent kubernetes object. If this is a Helm resource, this warning can be ignored.
      nonexistent-status-informer-object  warn   replicated-app.yaml  16     Status informer points to a nonexistent kubernetes object. If this is a Helm resource, this warning can be ignored.
      ```
      :::note
      You can ignore `info` or `warn` messages for the purpose of this quick start.
      :::

   1. From the `manifests` directory, create a release and promote it to the Unstable channel:

      ```bash
      replicated release create --yaml-dir . --promote Unstable
      ```
      **Example output**:
      ```bash
        • Reading manifests from . ✓
        • Creating Release ✓
          • SEQUENCE: 1
        • Promoting ✓
          • Channel 2kvjwEj4uBaCMoTigW5xty1iiw6 successfully set to release 1
      ```

1. Create a test customer so that you can install the release in your VM with Embedded Cluster:

   1. Log in to the [Vendor Portal](https://vendor.replicated.com).
   
   1. Under the application drop down, select the SlackerNews application that you created.

      <img alt="App drop down" src="/images/quick-start-app-dropdown-slackernews.png" width="250px"/>

      [View a larger version of this image](/images/quick-start-app-dropdown-slackernews.png)
   
   1. Click **Customers > Create customer**.

      On the **Create a new customer** page:

      1. For **Customer name**, enter a name for the customer. For example, `Example Customer`.

      1. For **Assigned Channel**, select **Unstable**. This allows the customer to install releases promoted to the Unstable channel.

      1. For **Customer type**, select **Development**.

      1. For **Install types**, enable **Embedded Cluster (current generation product)** and disable the other install type options.

      1. Click **Save Changes**.

1. (Optional) In the Vendor Portal, click **Releases** to view the release that you promoted. In the release viewer, you can see the Replicated manifests as well as the SlackerNews `Chart.yaml` and `values.yaml` files.

1. If you brought your own VM, SSH onto your VM. Otherwise, follow these steps to create and SSH onto a VM with CMX:

   1. Create an Ubuntu VM that expires after two hours:

      ```bash
      replicated vm create --distribution ubuntu --version 24.04 --instance-type r1.small --disk 50 --ttl 2h
      ```
      **Example output:**
      ```bash
      ID        NAME             DISTRIBUTION  VERSION  STATUS  NETWORK   CREATED               EXPIRES   COST
      97940819  practical_black  ubuntu        24.04    queued  c726ffff  2025-08-20 14:32 MDT  -         $0.69
      ```

   1. After a few minutes, check the status of the VM to see when it is `running`:

      ```bash
      replicated vm ls
      ```
      **Example output:**
      ```bash
      ID        NAME             DISTRIBUTION  VERSION  STATUS   NETWORK   CREATED               EXPIRES                COST
      97940819  practical_black  ubuntu        24.04    running  c726ffff  2025-08-20 14:32 MDT  2025-08-20 16:33 MDT   $0.69
      ```
   
   1. Run the following command to use the CMX Forwarder to SSH onto the VM:

      ```bash
      ssh VM_ID@replicatedvm.com
      ```
      Where `VM_ID` is the `ID` from the output of the `replicated vm ls` command.

      :::note
      If you are prompted to add the fingerprint for replicatedvm.com, type `yes` and press Enter.
      :::

   1. When prompted, provide the passphrase for the SSH key in your linked GitHub account. CMX uses GitHub SSH to provide access to the VM. 

1. Install the application with Embedded Cluster:
     
    1. In the Vendor Portal, on the page for the customer that you created, click **Embedded Cluster install instructions**.

       ![Customer install instructions](/images/quick-start-slackernews-customer-install-instructions-button.png)

       [View a larger version of this image](/images/quick-start-slackernews-customer-install-instructions-button.png)

    1. On your VM, run the commands in the **Embedded cluster install instructions** dialog to download the latest release, extract the installation assets, and install SlackerNews.

       <img width="600px" src="/images/embedded-cluster-install-dialog-latest.png" alt="embedded cluster install instructions dialog"/>

       [View a larger version of this image](/images/embedded-cluster-install-dialog-latest.png)

   1. When prompted by the `install` command, set a password for accessing the Admin Console.
   
      :::note
      Remember this password because you will use it to log in to the Admin Console in a later step.
      :::

      The installation command takes a few minutes to complete.

      **Example output:**

      ```bash
      ? Set the Admin Console password (minimum 6 characters): ********
      ? Confirm the Admin Console password: ********

      ✔  Initialization complete
      ✔  Host preflights passed
      ✔  Node is ready
      ✔  Storage is ready
      ✔  Runtime Operator is ready
      ✔  Admin Console is ready
      ✔  Additional components are ready

      -----------------------------------------------------------------
      Visit the Admin Console to configure and install slackernews-gnu:

      http://10.0.0.11:30000
      -----------------------------------------------------------------
      ```

      At this point, the cluster is provisioned and the Admin Console is deployed, but the application is not yet installed.

   1. Do one of the following to access the Admin Console:
       * If you brought your own VM, go to the URL provided in the output of the install command. 
          :::note
          The IP address in the provided URL might be the private IP address of your VM. You might need to edit the URL to use the public IP address.
          :::
       * If you created the VM with CMX:
         1. In the Vendor Portal, go to [**Compatibility Matrix**](https://vendor.replicated.com/compatibility-matrix/overview).
         1. Open the dot menu for your VM and click **Edit VM**.

            ![Edit VM in the dot menu](/images/compatibility-matrix-edit-vm.png)

            [View a larger version of this image](/images/compatibility-matrix-edit-vm.png)

         1. Under **Ingress & Ports**, for **Add DNS record**, click **Add** to create a DNS record using the default settings. This will provide a hostname where you can access the Admin Console.

         1. Add another DNS record with a **Target Port** of **443**. This will provide a hostname where you can access SlackerNews after it's installed.

             The following shows an example of both of these DNS records added to a VM:

             ![DNS record for a VM](/images/compatibility-matrix-dns-record.png)
             [View a larger version of this image](/images/compatibility-matrix-dns-record.png)

         1. (Optional) Copy the URL for the DNS record with a target port of 443 and save it somewhere on your local machine. You will be asked to provide this hostname in the Admin Console as part of the installation process for SlackerNews.
         
         1. In a browser, navigate to the URL for the DNS record with a target port of 30000.

            The Admin Console opens.
   
   1. If you are directed to the Admin Console landing page:
       1. Click **Start**.
       1. On the **Secure the Admin Console** screen, review the instructions and click **Continue**. In your browser, follow the instructions that were provided on the **Secure the Admin Console** screen to bypass the warning.
       1. On the **Certificate type** screen, either select **Self-signed** to continue using the self-signed Admin Console certificate or click **Upload your own** to upload your own private key and certificate. By default, a self-signed TLS certificate is used to secure communication between your browser and the Admin Console. You will see a warning in your browser every time you access the Admin Console unless you upload your own certificate.

       :::note
       If you created the VM with CMX, you might not see these pages. Instead, your browser might automatically redirect to the Admin Console login screen.
       :::
   
   1. On the login screen, enter the Admin Console password that you set and click **Log in**.

       ![Admin console login screen](/images/quick-start-slackernews-admin-console-login.png)

       [View a larger version of this image](/images/quick-start-slackernews-admin-console-login.png)

   1. On the **Configure the cluster** screen, you can view details about the VM where you installed, including its node role, status, CPU, and memory. Users can also optionally add additional nodes on this page before deploying the application. Click **Continue**. 

       ![Admin console login screen](/images/quick-start-slackernews-configure-the-cluster.png)

       [View a larger version of this image](/images/quick-start-slackernews-configure-the-cluster.png)

   1. On the **Configure SlackerNews** screen, complete the following fields:
   
       * For **Ingress Hostname**:
          * If you created the VM with CMX, enter the URL that you copied for the DNS record with a target port of 443. For example, `pensive-keldysh.ingress.replicatedcluster.com`.

          * If you are using your own VM, provide the public IP address for your VM. You can copy the IP address from your browser's address bar where you are currently accessing the Admin Console. Alternatively, if you have a hostname for your VM's IP address, you can provide that instead.

       * For **Application Access > Service Type**, ensure that **NodePort** is selected. Leave the **Node Port** field blank to use the default port of 443. 
       
       * For **Postgresql**, ensure that **Deploy Postgresql Database** is enabled to deploy the built-in Postgresql database.

       Leave the other fields on the **Configure SlackerNews** screen empty. They are not applicable for this quick start.

       ![Configure slackernews screen](/images/quick-start-slackernews-configure-slackernews.png)

       [View a larger version of this image](/images/quick-start-slackernews-configure-slackernews.png)
   
   1. Click **Continue**.

       The Admin Console dashboard opens. The application status changes from Missing to Unavailable while the application is being deployed. You can click **Details** next to the status for more information about the status of specific application resources.

       After a few minutes, the status changes to Ready when the application is deployed:

       ![Slackernews with ready status on admin console dashboard](/images/quick-start-slackernews-adm-dashboard-ready.png)

       [View a larger version of this image](/images/quick-start-slackernews-adm-dashboard-ready.png)

   1. After a few minutes when the application status is Ready, click **Open SlackerNews** to view SlackerNews in a browser.

       ![Slackernews landing page](/images/slackernews-landing-page.png)

       [View a larger version of this image](/images/slackernews-landing-page.png)

1. View telemetry for the instance that you just deployed:

    1. Return to the Vendor Portal, select the SlackerNews application, and go to **Customers**. Under the name of the customer, confirm that you can see an active instance.

       ![Slackernews instance on customers page](/images/quick-start-slackernews-customer-instance.png)

       [View a larger version of this image](/images/quick-start-slackernews-customer-instance.png)

       This instance telemetry is automatically collected and sent back to the Vendor Portal by Embedded Cluster and the Replicated SDK. For more information, see [About Instance and Event Data](/vendor/instance-insights-event-data).

    1. (Optional) Under **Instance ID**, click on the ID to view additional insights including the versions of Kubernetes and the Replicated SDK running in the cluster where you installed the application. 

1. Create a new release that adds preflight checks to the application:

   1. In your local filesystem, go to the `quick-start/chart/slackernews` directory.

   1. Create a `preflights.yaml` file in the `templates` directory for the chart:

      ```
      touch templates/preflights.yaml
      ```

   1. In the `preflights.yaml` file, add the following YAML to create a Kubernetes Secret with a simple preflight spec: 

      ```yaml
      apiVersion: v1
      kind: Secret
      metadata:
        name: slackernews-preflight
        labels:
          troubleshoot.sh/kind: preflight
      stringData:
        preflight.yaml: |-
          apiVersion: troubleshoot.sh/v1beta2
          kind: Preflight
          metadata:
            name: slackernews-preflight
          spec:
            collectors:
              - clusterInfo: {}
              - clusterResources: {}
              - http:
                  collectorName: slack
                  get:
                    url: https://api.slack.com/methods/api.test
                    timeout: 20s
            analyzers:
              # verify that slack is accessible
              - textAnalyze:
                  checkName: Slack Accessible
                  fileName: slack.json
                  regex: '"status": 200,'
                  outcomes:
                    - pass:
                        when: "true"
                        message: "Can access the Slack API"
                    - fail:
                        when: "false"
                        message: "Cannot access the Slack API. Check that the server can reach the internet and check [status.slack.com](https://status.slack.com)."
      ```
      
      The YAML above defines a preflight check that confirms that an HTTP request to the Slack API at `https://api.slack.com/methods/api.test` made from the cluster returns a successful response of `"status": 200,`.

   1. In the `Chart.yaml` file, increment the version to 1.0.1:

      ```yaml
      # Chart.yaml
      version: 1.0.1
      ```

   1. Update dependencies and package the chart to a `.tgz` chart archive:

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

   1. Move the `slackernews-1.0.1.tgz` chart archive to the `manifests` directory:

      ```bash
      mv slackernews-1.0.1.tgz ../../manifests
      ```

   1. Change to the `manifests` directory.   

   1. In the `manifests` directory, open the HelmChart custom resource (`slackernews.yaml`) and update the `chartVersion` to match the new archive:

      ```yaml
      # slackernews.yaml HelmChart
      chartVersion: 1.0.1
      ```  

   1. Remove the unused chart archive for version 1.0.0 from the `manifests` directory:

      ```
      rm slackernews-1.0.0.tgz
      ```        

   1. From the `manifests` directory, lint the release manifests:

      ```bash
      replicated release lint --yaml-dir .
      ```

      :::note
      If you see a `helm-archive-missing` error, confirm that you have only one chart archive in `manifests` for version 1.0.1, and that the `chartVersion` in the HelmChart resource is also set to `1.0.1`. If you notice that any of the errors are from files in your `.history/` directory, run `rm -rf .history` and then lint the files again.
      :::
   
   1. Create and promote a new release, setting the version label of the release to `0.0.2`:  

      ```bash
      replicated release create --yaml-dir . --promote Unstable --version 0.0.2
      ```
      **Example output**:
      ```bash
        • Reading manifests from . ✓
        • Creating Release ✓
          • SEQUENCE: 2
        • Promoting ✓
          • Channel 2kvjwEj4uBaCMoTigW5xty1iiw6 successfully set to release 2
      ```

1. On your VM, update the application instance to the new version that you just promoted:

   1. In the Admin Console, go to the **Version history** tab.

      The new version is displayed automatically.

      ![Admin console version history tab](/images/quick-start-slackernews-available-updates.png)

      [View a larger version of this image](/images/quick-start-slackernews-available-updates.png)

   1. Click **Deploy** next to the new version.

      The Embedded Cluster upgrade wizard opens. Follow the steps until you see the **Preflight checks** screen.

   1. On the **Preflight checks** screen, note that the "Slack Accessible" preflight check that you added was successful. Click **Next: Confirm and deploy**.   

      ![preflights screen in the upgrade wizard](/images/quick-start-slackernews-preflight.png)

      [View a larger version of this image](/images/quick-start-slackernews-preflight.png)  

   1. On the **Confirm and Deploy** page, click **Deploy**.

1. Clean up the installation:

    * If you created the VM with CMX, delete the VM:

       ```bash
       replicated vm rm VM_ID
       ```
       Where VM_ID is the ID of the VM. You can run `replicated vm ls` to get the ID.

    * If you brought your own VM, use Embedded Cluster to reset and reboot the VM to remove the installation:

       ```bash
       sudo ./APP_SLUG reset
       ```
       Where `APP_SLUG` is the unique slug for the application.

       :::note
       You can find the application slug by running replicated app ls on your local machine.  
       ::: 

## Next steps

Congratulations! As part of this quick start, you:
* Added the Replicated SDK to a Helm chart
* Created a release with the Helm chart
* Installed the release on a VM with Embedded Cluster
* Viewed telemetry for the installed instance in the Vendor Portal
* Created a new release to add preflight checks to the application
* Updated the application to the new version using Embedded Cluster

Now that you are familiar with the workflow of creating, installing, and updating releases, you can begin onboarding your own application to the Replicated Platform.

To get started, see [Onboard to the Replicated Platform](replicated-onboarding).

## Related topics

For more information about the Replicated Platform features mentioned in this quick start, see:

* [Embedded Cluster Overview](/vendor/embedded-overview)
* [About CMX](/vendor/testing-about)
* [About Preflight Checks and Support Bundles](/vendor/preflight-support-bundle-about)
* [About the Replicated SDK](/vendor/replicated-sdk-overview)
* [Manage Releases with the CLI](/vendor/releases-creating-cli)
* [Package a Helm Chart for a Release](/vendor/helm-install-release)