Skip to main content
Start with the Runloop Quickstart to use the examples below.
When you create a Devbox using a standard Runloop image, you get a fully functional but generic Linux installation. To prepare things for your workload, you then often need to install tools and libraries, download data, and perform other setup actions. Blueprints allow you to optimize your workflows by specifing these startup actions once, and using them across many Devboxes. When you create a Blueprint, Runloop runs your startup actions and saves the resulting Devbox image. When you later create a Devbox from this Blueprint, it starts with a fully configured environment. By building a blueprint, you get:
  1. Standardization: Define tools, binaries, and configurations your AI agent needs at runtime.
  2. Consistency: Ensure reproducible AI behavior across Devboxes.
  3. Efficiency: Reduce Devbox startup time by pre-installing tools.
  4. Customization: Tailor environments to specific AI-assisted development needs.
When should I use a Blueprint vs. a Snapshot?Snapshots and Blueprints both allow you to run devboxes with customizations. Blueprints are built programmatically and are cacheable using Docker layers, while Snapshots can be created quickly from an existing devbox.Examples:
  • Blueprint: You have a coding agent that is performing a task that requires installing a specific tool. Create a Blueprint with set-up steps for the tool. All Devboxes you launch from that Blueprint will have the environment already set up, and will not incur installation or setup time.
  • Snapshot: You have a coding agent in a devbox considering 3 different ways to complete a task. Create a snapshot of the initial state of the devbox, create 3 parallel devboxes from that snapshot, collate the results, and then choose the best option to continue.

Creating a Blueprint

One use case for a blueprint is preinstalling tools your AI agent may want to use. For example, let’s create a simple Blueprint that installs jq, a lightweight command-line JSON processor:
blueprint = await runloop.blueprints.create(
  name="docs-template",
  launch_parameters={
    "launch_commands": ["sudo apt install -y jq"]
  }
)
devbox = await blueprint.create_devbox()
print(f"Devbox created with ID: {devbox.id}" +
      f"from blueprint {blueprint.id}")
Use the Debian package manager (apt) for installing system packages on the Runloop base image.

Custom files and folders

Basic Configuration with file mounts

To add individual files to your Blueprint, you can use the file_mounts parameter. The key of the file_mounts object is the path to the file in the devbox and the value is the content of the file. By default, blueprints are constructed by user and /home/user/ is where files are allowed to be mounted.
blueprint = await runloop.blueprints.create(
  name="python-filemount",
  file_mounts={
    "/home/user/script.py": "print(\"Hello, world!\")"
  }
)
If you want to mount files to root, you will need to specify building the blueprint as root via launch_parameters
blueprint = await runloop.blueprints.create(
  name="python-root-filemount",
  file_mounts={
    "/script.py": "print(\"Hello, world!\")"
  },
  launch_parameters={
    "user_parameters": {
      "username": "root",
      "uid": 0
    }
  }
)

Basic Configuration with CodeMounts

To add a CodeMount to your Blueprint:
blueprint = await runloop.blueprints.create(
  name="fe-bot",
  code_mounts=[{
    "repo_name": "runloop-fe",
    "repo_owner": "runloop",
    "token": os.environ.get("GH_TOKEN")
  }]
)
print(f"Blueprint created with ID: {blueprint.id}")
This creates a Blueprint named “fe-bot” that includes the “runloop-fe” repository.

Private Repository Authentication

For private repositories, include a GitHub Personal Access Token (PAT):
blueprint = await runloop.blueprints.create(
  name="fe-bot",
  code_mounts=[{
    "repo_name": "runloop-fe",
    "repo_owner": "runloop",
    "token": os.environ.get("GH_TOKEN")
  }]
)
This sets up the necessary environment for immediate use of Git and GitHub tools.

The Blueprint Build Process

When you create a Blueprint, Runloop builds a custom image containing all of your specified tools and configurations. The status field indicates the current state of your Blueprint:

Checking Build Status

After creating a Blueprint, check its build status:
blueprint = await runloop.blueprints.from_id(blueprint.id)
info = await blueprint.get_info()
print(f"Blueprint status: {info.status}")

# Check logs for build errors
logResult = await blueprint.logs()
for log in logResult.logs:
  print(f"{log.level}: {log.message}")
  

Advanced Usage: Creating a Blueprint from a Public Image Registry

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

Example: Docker Hub Image (wordpress)

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
blueprint = await runloop.blueprints.create(
  name="wordpress-bpt",
  dockerfile="FROM wordpress"
)

Example: Public ECR Image (runloop-dnd-image)

For more information about Docker-in-Docker support and available images, see Running Docker on a Devbox.
blueprint = runloop.blueprints.create(
  name="runloop-dnd-bpt",
  dockerfile="FROM runloop:runloop/prod-dnd-arm64"
)

Advanced Usage: 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 of your Runloop Dashboard or via the SDK
  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

Example: Using a npm token to install a private package

This example assumes you have a npm token with the read:packages scope.
blueprint = await runloop.blueprints.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"}
)

Advanced Usage: 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:
    FROM runloop:runloop/starter-arm64
    
    The Runloop base image is public and can be downloaded for local testing.
  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

Advanced Usage: Composable Blueprints

You can create a Blueprint using another Blueprint as the base. This is useful when you want to create a Blueprint that builds upon other Blueprints.
blueprint = await runloop.blueprints.create(
  name="fe-bot",
  base_blueprint_id="bpt_123456789"
)
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
FROM runloop:bpt_123456789

# Rest of Dockerfile

Advanced Usage: 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.
dockerfile = """
FROM ubuntu:22.04
USER root
WORKDIR /root
RUN adduser -uid 9999 newdevboxuser
USER newdevboxuser
"""

blueprint = await runloop.blueprints.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}")

Advanced Usage: Customization via Launch Parameters

Because Blueprints map to images that launch devboxes, we share some of the same launch parameters as devboxes. See these pages for more customizable parameters:

Deleting Blueprints

By default, Blueprints persist indefinitely and continue to incur storage costs. To optimize resource usage and costs, you can delete Blueprints that are no longer needed.

Deleting a Single Blueprint

To delete a specific Blueprint, use its ID:
blueprint = await runloop.blueprints.from_id(blueprint_id)
await blueprint.delete()

Cleaning Up Old Blueprint Versions

When you create multiple versions of a Blueprint with the same name, you may want to delete older versions to reduce storage costs. Here’s how to keep only the latest version:
# Create a new blueprint
new_blueprint = await runloop.blueprints.create(
  name="my_blueprint_name",
  launch_parameters={"launch_commands": ["sudo apt install -y jq"]}
)

# Get all blueprints with the same name
blueprints = await runloop.blueprints.list(name='my_blueprint_name')

# Delete all older blueprints, keeping only the newest one
for blueprint in blueprints:
  if blueprint.id != new_blueprint.id:
    await blueprint.delete()


Be careful when deleting Blueprints, as this action cannot be undone. Ensure you’re not deleting Blueprints that you may need later.

Best Practices

  1. Start Simple: Begin with basic Blueprints and gradually add complexity.
  2. Test Manually Using SSH: You can create a devbox and SSH into it and manually install tools to make sure the commands are correct before layering them into Blueprints.
  3. Lookup Blueprints via blueprint_name instead of blueprint_id to ensure you are using the latest version for a particular name. Use specific Blueprint IDs only when you need version control for particular setups.
  4. Implement setup_commands in your Devbox creation to keep code and dependencies up-to-date.
  5. Regularly update your Blueprints with the latest repository changes.
  6. Delete unused / deprecated Blueprints.
By leveraging Blueprints effectively, you can create optimized, consistent environments for your AI-assisted software engineering tasks, enhancing productivity and reliability in your development process.