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

# Dockerfile Customization

> Create Blueprints using custom Dockerfiles, public registries, secrets, and composable blueprints

<Tip>Start with the [Runloop Quickstart](/docs/tutorials/quickstart) to use the examples below.</Tip>

<Note title="Starter image">
  The starter image is used when you start a devbox without specifying an image, and as the default base when you build a blueprint without providing Dockerfile content. It includes:

  * **Core tools:** jq, sudo
  * **Extras:** dnsutils, iputils-ping, less, vim, rsync,
    gh
  * **Python stack:** Python 3.12, pip, uv
  * **Node stack:** Node 22.15.0, npm, Yarn 1.22.22 via
    corepack
</Note>

## Creating Blueprints from Public Image Registries

Runloop supports creating Blueprints from public image registries. This is useful when you want to use a specific version of a tool or framework.

### Docker Hub Images

This process works with any image that is available on Docker Hub. The following example creates a Blueprint from the [`wordpress` image at Docker Hub](https://hub.docker.com/_/wordpress):

<CodeGroup>
  ```python Python theme={null}
  blueprint = await runloop.blueprint.create(
    name="wordpress-bpt",
    dockerfile="FROM wordpress"
  )
  ```

  ```typescript TypeScript theme={null}
  const blueprint = await runloop.blueprint.create({
    name: "wordpress-bpt",
    dockerfile: "FROM wordpress"
  });
  ```
</CodeGroup>

### Public ECR Images

For more information about Docker-in-Docker support and available images, see [Running Docker on a Devbox](/docs/devboxes/capabilities/docker-in-docker).

<CodeGroup>
  ```python Python theme={null}
  blueprint = await runloop.blueprint.create(
    name="runloop-dnd-bpt",
    dockerfile="FROM runloop:runloop/prod-dnd-arm64"
  )
  ```

  ```typescript TypeScript theme={null}
  const blueprint = await runloop.blueprint.create({
    name: "runloop-dnd-bpt",
    dockerfile: "FROM runloop:runloop/prod-dnd-arm64"
  });
  ```
</CodeGroup>

## Using Secrets in Blueprints

You can securely inject secrets (like API tokens, credentials, or SSH keys) into your blueprint build process. This is useful for accessing private resources during the build, such as cloning private repositories or downloading authenticated packages.

### Setting Up Secrets

1. **Create secrets** in the [Settings page](https://platform.runloop.ai/settings) of your Runloop Dashboard or [via the SDK](/docs/devboxes/configuration/account-secrets)
2. **Mount secrets** when creating a blueprint using the format `{ env_var_name: secret_name }`
3. **Use secrets** in either `dockerfile` or `system_setup_commands`

<Note>
  The `system_setup_commands` field has a safe limit of 32 KB. Larger inputs may cause Blueprints builds to fail.
</Note>

### Example: Using an npm token to install a private package

This example assumes you have an npm token with the `read:packages` scope.

<CodeGroup>
  ```python Python theme={null}
  blueprint = await runloop.blueprint.create(
    name="npm-token-blueprint",
    dockerfile="FROM ubuntu:22.04\n\nRUN npm install -g npm \nRUN npm install -g npm-cli-login \nRUN npm-cli-login -u \"myusername\" -p \"${NPM_TOKEN}\" -e \"ci@company.ai\" -r \"https://npm.pkg.github.com\" -s \"@myorg\"",
    secrets={"NPM_TOKEN": "npm-token"}
  )
  ```

  ```typescript TypeScript theme={null}
  const blueprint = await runloop.blueprint.create({
    name: "npm-token-blueprint",
    dockerfile: "FROM ubuntu:22.04\n\nRUN npm install -g npm \nRUN npm install -g npm-cli-login \nRUN npm-cli-login -u \"myusername\" -p \"${NPM_TOKEN}\" -e \"ci@company.ai\" -r \"https://npm.pkg.github.com\" -s \"@myorg\"",
    secrets: { NPM_TOKEN: "npm-token" }
  });
  ```
</CodeGroup>

## Custom Dockerfiles

For more complex environments, you can use a full Dockerfile as the basis for your Blueprint. This is useful when you need to install multiple tools or perform complex setup operations.

1. To avoid unnecessarily downloading full Linux installations, base your Dockerfile on the Runloop base image. Note that you must use the `runloop:` prefix:

   ```dockerfile theme={null}
   FROM runloop:runloop/starter-arm64
   ```

   <Note>
     The Runloop base image is public and can be downloaded for local testing.
   </Note>

2. When you create a Blueprint with your Dockerfile, Runloop will:
   * Use your Dockerfile as the base
   * Apply any `launch_parameters.launch_commands` specified
   * Set up any code mounts you have defined

## Composable Blueprints

Runloop supports multistage builds, allowing you to create a Blueprint using another Blueprint as the base. This enables layered configurations where common tooling can be shared across multiple specialized blueprints.

<CodeGroup>
  ```python Python theme={null}
  blueprint = await runloop.blueprint.create(
    name="composed-blueprint",
    base_blueprint_id="bpt_123456789"
  )
  ```

  ```typescript TypeScript theme={null}
  const blueprint = await runloop.blueprint.create({  
    name: "composed-blueprint",
    base_blueprint_id: "bpt_123456789"
  });
  ```
</CodeGroup>

Alternatively, you can achieve the same result by including a base blueprint using the standard `FROM` instruction via the `Dockerfile` parameter to specify the composable Blueprint for building:

```dockerfile theme={null}
FROM runloop:bpt_123456789

# Rest of Dockerfile
```

## Customizing the Base User

You can customize the base user for a Blueprint. SSH and execute commands to Devboxes created from this blueprint will be via the specified user.

<CodeGroup>
  ```python Python theme={null}
  dockerfile = """
  FROM ubuntu:22.04
  USER root
  WORKDIR /root
  RUN adduser -uid 9999 newdevboxuser
  USER newdevboxuser
  """

  blueprint = await runloop.blueprint.create(
    name="bp_custom_user",
    dockerfile=dockerfile,
    launch_parameters={
      "user_parameters": {
        "username": "newdevboxuser",
        "uid": 9999
      },
      "launch_commands": []
    },
  )

  print(f"Blueprint created with ID: {blueprint.id}")
  ```

  ```typescript TypeScript theme={null}
  const dockerfile = `
  FROM ubuntu:22.04
  USER root
  WORKDIR /root
  RUN adduser -uid 9999 newdevboxuser
  USER newdevboxuser
  `

  const blueprint = await runloop.blueprint.create({
    name: "bp_custom_user",
    dockerfile: dockerfile,
    launch_parameters: {
      user_parameters: {
        username: "newdevboxuser",
        uid: 9999,
      },
      launch_commands: [],
    },
  });

  console.log(blueprint.id);
  ```
</CodeGroup>

## Next Steps

* [Configure network policies](/docs/devboxes/blueprints/network-policies) - Restrict network access during build and runtime
* [Manage blueprint lifecycle](/docs/devboxes/blueprints/lifecycle) - Delete blueprints and configure launch parameters
* [Troubleshoot blueprint builds](/docs/devboxes/configuration/troubleshooting/troubleshooting-blueprints) - Debug failed builds
