---
title: "Deploy a Stateless Application on Kubernetes"
description: "In this tutorial, you'll run a stateless application on Kubernetes using a Deployment object, authored with Pulumi and TypeScript instead of YAML and the `kubectl` CLI. This gives you the full power of a familiar programming language combined with immutable infrastructure for a robust, repeatable update experience.

You'll deploy an Nginx container, inspect the resulting Kubernetes resources with `kubectl`, update the container image to see Pulumi's structured diff, and scale the application by changing a single configuration value."
url: "https://www.pulumi.com/dev/tutorials/stateless-app/"
image: "https://www.pulumi.com/assets/og/dev/tutorials/stateless-app.png"
---

# Deploy a Stateless Application on Kubernetes

In this tutorial, you'll run a stateless application on Kubernetes using a Deployment object, authored with Pulumi and TypeScript instead of YAML and the `kubectl` CLI. This gives you the full power of a familiar programming language combined with immutable infrastructure for a robust, repeatable update experience.

You'll deploy an Nginx container, inspect the resulting Kubernetes resources with `kubectl`, update the container image to see Pulumi's structured diff, and scale the application by changing a single configuration value.

## What you'll learn

- How to define a Kubernetes Deployment with Pulumi and TypeScript
- How Pulumi waits for a deployment to converge, unlike kubectl
- How to update a container image and review a structured diff before applying it
- How to scale an application by changing a single configuration value

## Prerequisites

- The [Pulumi CLI](https://www.pulumi.com/docs/install/)
- A [Pulumi Cloud account](https://app.pulumi.com/signup) and an [access token](https://www.pulumi.com/docs/administration/access-identity/access-tokens/)
- A working Kubernetes cluster ([Minikube](https://kubernetes.io/docs/getting-started-guides/minikube) is an easy way to get started)
- The Kubernetes command line tool [kubectl](https://kubernetes.io/docs/tasks/tools/) installed

In this tutorial, you'll run an application using a [Kubernetes Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) object. This results in an automatically scaled-out container running inside of a cluster.

This example is authored using Pulumi's programming model in TypeScript instead of YAML, and using the `pulumi` CLI for deployment rather than `kubectl`. This gives you the full power of familiar languages, combined with immutable infrastructure, delivering a robust and repeatable update experience.

This example is based on [this Kubernetes tutorial](https://kubernetes.io/docs/tasks/run-application/run-stateless-application-deployment/), and its full code is [available on GitHub](https://github.com/pulumi/examples/tree/master/kubernetes-ts-guestbook).

By the end, you'll have:

- Created a Deployment using Pulumi that runs Nginx
- Used `pulumi` to deploy this application
- Used `kubectl` to list information about the resulting Kubernetes resources
- Updated the deployment's number of replicas using `pulumi`
- Cleaned up

## Create and configure a project

Normally you would write YAML files to configure your services, and then run `kubectl` commands to create and manage them. Instead of doing that, you'll author your program in code and deploy it with `pulumi`.

To start, create a new Pulumi project from a template:

```shell
$ mkdir k8s-nginx && cd k8s-nginx
$ pulumi new kubernetes-typescript
```

This command initializes a fresh project in the newly created `k8s-nginx` directory.

Next, replace the minimal contents of the template's `index.ts` file with the code for the deployment:

```typescript
import * as pulumi from "@pulumi/pulumi";
import * as k8s from "@pulumi/kubernetes";

let config = new pulumi.Config();

let nginxLabels = { app: "nginx" };
let nginxDeployment = new k8s.apps.v1.Deployment("nginx-deployment", {
    spec: {
        selector: { matchLabels: nginxLabels },
        replicas: config.getNumber("replicas") || 2,
        template: {
            metadata: { labels: nginxLabels },
            spec: {
                containers: [{
                    name: "nginx",
                    image: "nginx:1.7.9",
                    ports: [{ containerPort: 80 }]
                }],
            },
        },
    },
});

export let nginx = nginxDeployment.metadata.apply(md => md.name);
```

This code simply creates a Kubernetes Deployment object. The entire Kubernetes object model is available to you, giving you the full power of Kubernetes right away.

## Deploy the application

Now you're ready to deploy your code. To do so, run `pulumi up`:

```shell
$ pulumi up
```

The command first shows a complete preview of what will take place, with a confirmation prompt. No changes are made yet. It should look something like this:

```
Previewing update of stack 'k8s-nginx-dev'
Previewing changes:

    Type                           Name                     Plan       Info
+   pulumi:pulumi:Stack            k8s-nginx-k8s-nginx-dev  create
+   └─ kubernetes:apps:Deployment  nginx-deployment         create

info: 2 changes previewed:
    + 2 resources to create

Do you want to perform this update?
> yes
  no
  details
```

Select "yes" and hit enter. The deployment proceeds, and the output looks like this:

```
Updating stack 'k8s-nginx-dev'
Performing changes:

    Type                           Name                     Status      Info
+   pulumi:pulumi:Stack            k8s-nginx-k8s-nginx-dev  created
+   └─ kubernetes:apps:Deployment  nginx-deployment         created

---outputs:---
nginx: "nginx-deployment-rlefbi4w"

info: 2 changes performed:
    + 2 resources created
Update duration: 8.127334048s

Permalink: https://app.pulumi.com/joeduffy/k8s-nginx-dev/updates/1
```

> **Note**
>
> Pulumi waits until the deployment has succeeded (or failed), and it prints detailed status outputs as the deployment happens. This is in contrast to `kubectl`, which returns immediately. The Pulumi CLI's approach ensures that you have more robust deployments that converge as expected.

## Inspect the Kubernetes resources

Now that you've done the deployment, check some state with `kubectl`:

```shell
$ kubectl describe deployment $(pulumi stack output nginx)
```

Notice that you used the `pulumi stack output` command to fetch the auto-generated deployment name.

The output will look similar to this:

```
Name:     nginx-deployment-rlefbi4w
Namespace:    default
CreationTimestamp:  Tue, 30 Aug 2018 18:11:37 -0700
Labels:     app=nginx
Annotations:    deployment.kubernetes.io/revision=1
Selector:   app=nginx
Replicas:   2 desired | 2 updated | 2 total | 2 available | 0 unavailable
StrategyType:   RollingUpdate
MinReadySeconds:  0
RollingUpdateStrategy:  1 max unavailable, 1 max surge
Pod Template:
  Labels:       app=nginx
  Containers:
   nginx:
    Image:              nginx:1.7.9
    Port:               80/TCP
    Environment:        <none>
    Mounts:             <none>
  Volumes:              <none>
Conditions:
  Type          Status  Reason
  ----          ------  ------
  Available     True    MinimumReplicasAvailable
  Progressing   True    NewReplicaSetAvailable
OldReplicaSets:   <none>
NewReplicaSet:    nginx-deployment-rlefbi4w-1771418926 (2/2 replicas created)
No events.
```

Notice the `StrategyType` is `RollingUpdate` with a `RollingUpdateStrategy` of `1 max unavailable, 1 max surge`. This is how Kubernetes rolls out changes to the deployment without downtime, replacing pods gradually.

Now list the pods created by this deployment:

```shell
$ kubectl get pods -l app=nginx
```

The output will look something like this:

```
NAME                                        READY     STATUS    RESTARTS   AGE
nginx-deployment-rlefbi4w-1771418926-7o5ns   1/1       Running   0          1m
nginx-deployment-rlefbi4w-1771418926-r18az   1/1       Running   0          1m
```

## Update the deployment

You can update your program by changing the source code and re-running `pulumi up`. Do two quick updates to see what this looks like. After that, you'll clean up and you're done!

First, update your version of Nginx from 1.7 to 1.8. Simply replace the line:

```typescript
                    image: "nginx:1.7.9",
```

with:

```typescript
                    image: "nginx:1.8",
```

and re-run `pulumi up`. You'll see a preview that indicates just the spec changed:

```
Previewing update of stack 'k8s-nginx-dev'
Previewing changes:

    Type                           Name                     Plan          Info
*   pulumi:pulumi:Stack            k8s-nginx-k8s-nginx-dev  no change
~   └─ kubernetes:apps:Deployment  nginx-deployment         update        changes: ~ spec

    ---outputs:---
    nginx: "nginx-deployment-rlefbi4w"

info: 1 change previewed:
    ~ 1 resource to update
    1 resource unchanged

Do you want to perform this update?
  yes
  no
> details
```

If you choose `details` and hit enter, you'll see a full diff of the changes:

```
* pulumi:pulumi:Stack: (same)
    [urn=urn:pulumi:k8s-nginx-dev::k8s-nginx::pulumi:pulumi:Stack::k8s-nginx-k8s-nginx-dev]
    ---outputs:---
    nginx: "nginx-deployment-rlefbi4w"
    ~ kubernetes:apps/v1:Deployment: (update)
        [id=default/nginx-deployment-rlefbi4w]
        [urn=urn:pulumi:k8s-nginx-dev::k8s-nginx::kubernetes:apps/v1:Deployment::nginx-deployment]
    ~ spec      : {
        ~ template: {
            ~ spec    : {
                ~ containers: [
                    ~ [0]: {
                            ~ image: "nginx:1.7.9" => "nginx:1.8"
                            }
                    ]
                }
            }
        }
```

If you select `yes` and hit enter to proceed with the update, the deployment is updated in place.

## Scale the application

Next, scale your application by increasing the replica count. Notice this example used the Pulumi configuration system so that the replica count can be easily changed. Simply run:

```shell
$ pulumi config set replicas 4
```

and re-run `pulumi up`. Pulumi figures out the minimal set of changes to make:

```shell
$ pulumi up
```

The output from running this command will look like the usual update, with a preview diff, prompt, and details.

## Clean up

From here, feel free to experiment. As soon as you're done, clean up your stack:

```shell
$ pulumi destroy --yes
$ pulumi stack rm --yes
```

Afterwards, query the list of pods to verify that none are remaining:

```shell
$ kubectl get pods -l app=nginx
```

This should print out something along these lines:

```
No resources found.
```

</content>
</invoke>
