---
title: "Creating Docker Images"
description: "Learn how to use Pulumi and the Docker provider to create Docker images locally in this tutorial."
url: "https://www.pulumi.com/dev/tutorials/pulumi-fundamentals/create-docker-images/"
image: "https://www.pulumi.com/assets/og/dev/tutorials/pulumi-fundamentals/create-docker-images.png"
---

# Creating Docker Images

Learn how to use Pulumi and the Docker provider to create Docker images locally in this tutorial.

In this part, you'll create your first Pulumi _resource_. Resources in Pulumi are the basic building blocks of your infrastructure, whether that's a database
instance or a compute instance or a specific storage bucket. In Pulumi, _resource providers_ manage your resources. You can group those resources to
abstract them (such as a group of compute instances that all have the same configuration and implementation) via component resources.

In this case, the resources you're provisioning will be Docker containers and images that you build locally with Pulumi infrastructure as code. The resource provider is Docker, and you'll be using Node.js as your _language host_, or the executor that compiles the code that you write and interprets it for Pulumi.

> **Note**
>
> Note that we're only using Docker here to make it easier to learn about Pulumi (mainly because setting up a new account on any of the cloud providers can take
> some time and can introduce a lot of complexity). Most users are using one of the major cloud providers to build with Pulumi.

## Verify your application

Let's explore the app you'll be creating. In a browser, open the [pulumi/tutorial-pulumi-fundamentals repository](https://github.com/pulumi/tutorial-pulumi-fundamentals) on GitHub. In this repository, you'll find a `frontend`, `backend`, and `data` directory. Each directory contains a `Dockerfile` that builds the image for that portion of the application.

Let's examine the backend `Dockerfile` in `backend/Dockerfile`:

```docker
FROM node:14

# Create app directory
WORKDIR /usr/src/app

COPY ./src/package*.json ./

RUN npm install
COPY ./src .
RUN npm build
EXPOSE 3000

CMD [ "npm", "start" ]
```

This `Dockerfile` copies the REST backend into the Docker filesystem, installs the dependencies, and builds the image. Note that port 3000 must be open on your
host machine.

> **Note**
>
> For the purposes of this topic, we've already built the images. We are showing the code for the application here just for completeness; you won't actually need to do anything with this code.

## Pull a Docker Image with Pulumi

**TypeScript**

Before you start writing a Pulumi program, you need to install a resource provider. In this case, you'll need the `@pulumi/docker` provider for Node.js. Let's install the provider now:

```bash
npm install @pulumi/docker
```

**Python**

Before you start writing a Pulumi program, you need to install a resource provider. In this case, you'll need the `pulumi_docker` provider for Python. It's always good practice for Python to work inside a virtual environment, or venv, so let's activate our venv and use `pip` to install the
provider along with the main Pulumi package:

```bash
cd ../
source venv/bin/activate
pip3 install pulumi_docker
```

You should see output showing the provider package being installed, just like for any Python package install. Add the package to the `requirements.txt` file
by adding `pulumi_docker` on a new line at the end of the file.

Back inside your Pulumi program, you're ready to pull your first Docker image. Remember that a Pulumi program is the code that defines the desired state of your
infrastructure using a general-purpose programming language. In this case, you're using TypeScript, so the program file is `index.ts`. In `index.ts`, use any editor to add the following code:

```typescript
import * as pulumi from "@pulumi/pulumi";
import * as docker from "@pulumi/docker";

const stack = pulumi.getStack();

// Pull the backend image
const backendImageName = "backend";
const backend = new docker.RemoteImage(`${backendImageName}Image`, {
    name: "pulumi/tutorial-pulumi-fundamentals-backend:latest",
});
```

```python
import pulumi
import pulumi_docker as docker

stack = pulumi.get_stack()

# Pull the backend image
backend_image_name = "backend"
backend = docker.RemoteImage(
    f"{backend_image_name}_image",
    name="pulumi/tutorial-pulumi-fundamentals-backend:latest",
)
```

**TypeScript**

In this file, we import the main Pulumi package and the Docker provider. Then, we figure out which stack we're operating against, and populate the `stack` variable for later use. When we pull our backend image, we give it a name in our stack as "backendImage" before passing some arguments to the Docker provider. The Docker provider uses the `name` argument to pull a remote image for us to use.

Notice that we're mixing in some language constructs in here like string interpolation. With Pulumi, we have access to the full language ecosystem, including built-ins and third-party libraries. Pulumi also has typing support, so you can use the tools in your favorite IDE, like auto-completion, to verify that you're using the correct types for any inputs you're using.

**Python**

In this file, we import the main Pulumi package and the Docker provider. Then, we figure out which stack we're operating against, and populate the `stack` variable for later use. When we pull our backend image, we give it a name in our stack as "backend_image" before passing some arguments to the Docker provider. The Docker provider uses the `name` argument to pull a remote image for us to use.

Notice that we're mixing in some Python constructs in here like f-strings (string interpolation). With Pulumi, we have access to the full language ecosystem, including third-party libraries. Pulumi also has typing support, so you can use the tools in your favorite IDE, like auto-completion, to verify that you're using the correct types for any inputs you're using.

Run `pulumi up`.

> **Note**
>
> Note that it make take a bit before you get any output because Docker is doing a
> lot of work in the background before it connects to Pulumi. Be patient!

Pulumi should pull your Docker image. First, though, it
gives you a preview of the changes you'll be making to the stack and asks if the
changes appear okay to you. You'll need to reply "yes" to the prompt to actually
pull the image. After the command finishes, you will see your image if you run
the command `docker images` or `docker image ls` (depending on your preference).

Let's dig a bit deeper into the code and explore the various Pulumi concepts.
Every resource has _inputs_ and _outputs_. Inputs are values that are
provided to the resource. Outputs are the resource's properties. Note that
Pulumi can't know the output until the resource has completed provisioning as
some of those outputs are provided by the provider after everything has loaded,
booted, or otherwise has come online. More on outputs later.

In our case here, the Docker
[`RemoteImage`](https://www.pulumi.com/registry/packages/docker/api-docs/remoteimage) resource
takes the following inputs:

**Python**

- an unnamed string: a name for the resource we are creating
- `name`: the name of the remote image to pull down

**TypeScript**

- an unnamed string: a name for the resource we are creating
- `name`: the name of the remote image to pull down

Now that you've provisioned the first piece of infrastructure, you can continue adding the other components of the application.

## Add the frontend client and MongoDB

The application we're building includes a frontend client and a MongoDB database. You'll add them both to the program next. Add the following code after the previous fragment:

```typescript
// Pull the frontend image
const frontendImageName = "frontend";
const frontend = new docker.RemoteImage(`${frontendImageName}Image`, {
    name: "pulumi/tutorial-pulumi-fundamentals-frontend:latest",
});

// Pull the MongoDB image
const mongoImage = new docker.RemoteImage("mongoImage", {
    name: "pulumi/tutorial-pulumi-fundamentals-database:latest",
});
```

```python
# Pull the frontend image
frontend_image_name = "frontend"
frontend = docker.RemoteImage(
    f"{frontend_image_name}_image",
    name="pulumi/tutorial-pulumi-fundamentals-frontend:latest",
)

# Pull the MongoDB image
mongo_image = docker.RemoteImage(
    "mongo_image", name="pulumi/tutorial-pulumi-fundamentals-database:latest"
)
```

This pulls the frontend client and the populated database images in the same way you pulled the backend image earlier.

Compare your program now to the completed program below before moving forward:

```typescript
import * as pulumi from "@pulumi/pulumi";
import * as docker from "@pulumi/docker";

const stack = pulumi.getStack();

// Pull the backend image
const backendImageName = "backend";
const backend = new docker.RemoteImage(`${backendImageName}Image`, {
    name: "pulumi/tutorial-pulumi-fundamentals-backend:latest",
});

// Pull the frontend image
const frontendImageName = "frontend";
const frontend = new docker.RemoteImage(`${frontendImageName}Image`, {
    name: "pulumi/tutorial-pulumi-fundamentals-frontend:latest",
});

// Pull the MongoDB image
const mongoImage = new docker.RemoteImage("mongoImage", {
    name: "pulumi/tutorial-pulumi-fundamentals-database:latest",
});
```

```python
import pulumi
import pulumi_docker as docker

stack = pulumi.get_stack()

# Pull the backend image
backend_image_name = "backend"
backend = docker.RemoteImage(
    f"{backend_image_name}_image",
    name="pulumi/tutorial-pulumi-fundamentals-backend:latest",
)

# Pull the frontend image
frontend_image_name = "frontend"
frontend = docker.RemoteImage(
    f"{frontend_image_name}_image",
    name="pulumi/tutorial-pulumi-fundamentals-frontend:latest",
)

# Pull the MongoDB image
mongo_image = docker.RemoteImage(
    "mongo_image", name="pulumi/tutorial-pulumi-fundamentals-database:latest"
)
```

If your code looks the same, great! Otherwise, update yours to match this code.

Now, run `pulumi up` to pull all of the images that we'll need.

> **Note**
>
> Note that, in the future, you don't need to run `pulumi up` in stages like this
> to create your infrastructure. You can write the entire program and then run it.
> We're only doing a step-by-step process here to make learning easier.

From here, we can move on to configuring and provisioning our containers.
