Skip to content
ICP CLI
Feedback welcome! Report issues on GitHub, ask questions on the Forum, or chat with us on Discord.

Creating Recipes

Recipes are reusable build templates that you can create to encode your team’s build conventions or share them with the community.

Recipe File Structure

A recipe is a handlebars template that renders to yaml and contains the build and sync steps of a canister configuration.

{{! # recipes/my-recipe.hbs }}
build:
steps:
- type: script
commands:
- echo "Building {{ name }}..."
{{! # optional sync step }}
sync:
steps:
- type: script
commands:
- echo "Syncing {{ name }}..."

Basic Recipe Example

A simple recipe for Rust builds:

{{! file: ./recipes/rust-example.hbs }}
{{! A recipe for building a rust canister }}
{{! `package: string` The package to build }}
{{! `shrink: boolean` Optimizes the wasm with ic-wasm }}
build:
steps:
- type: script
commands:
- cargo build --package {{ package }} --target wasm32-unknown-unknown --release
- mv target/wasm32-unknown-unknown/release/{{ replace "-" "_" package }}.wasm "$ICP_WASM_OUTPUT_PATH"
- type: script
commands:
- command -v ic-wasm >/dev/null 2>&1 || { echo >&2 'ic-wasm not found. To install ic-wasm, see https://github.com/dfinity/ic-wasm \n'; exit 1; }
- ic-wasm "$ICP_WASM_OUTPUT_PATH" -o "${ICP_WASM_OUTPUT_PATH}" metadata "cargo:version" -d "$(cargo --version)" --keep-name-section
- ic-wasm "$ICP_WASM_OUTPUT_PATH" -o "${ICP_WASM_OUTPUT_PATH}" metadata "template:type" -d "rust" --keep-name-section
{{#if shrink}}
- ic-wasm "$ICP_WASM_OUTPUT_PATH" -o "${ICP_WASM_OUTPUT_PATH}" shrink --keep-name-section
{{/if}}

Usage:

icp.yaml
canisters:
- name: backend
recipe:
type: ./recipes/rust-example.hbs
configuration:
package: my-backend-crate
shrink: true

Template Syntax

Recipes use Handlebars templating:

Variables

Access configuration parameters passed in the configuration section of the recipe.

build:
steps:
- type: script
commands:
- cargo build --package {{configuration.package}}

Conditionals

Use {{#if}} for optional configuration:

build:
steps:
- type: script
commands:
{{#if shrink}}
- cargo build --release --target wasm32-unknown-unknown
- ic-wasm target/wasm32-unknown-unknown/release/{{configuration.package}}.wasm -o "$ICP_WASM_OUTPUT_PATH" shrink
{{else}}
- cargo build --target wasm32-unknown-unknown
- cp target/wasm32-unknown-unknown/debug/{{configuration.package}}.wasm "$ICP_WASM_OUTPUT_PATH"
{{/if}}

Loops

Use {{#each}} for dynamic lists:

{{! file: ./recipes/rust-example-metadata.hbs }}
{{! A recipe for building a rust canister }}
{{! `package: string` The package to build }}
{{! `metadata: [name: string, value: string]`: An array of name/value pairs that get injected into the wasm metadata section }}
build:
steps:
- type: script
commands:
- cargo build --package {{ package }} --target wasm32-unknown-unknown --release
- mv target/wasm32-unknown-unknown/release/{{ replace "-" "_" package }}.wasm "$ICP_WASM_OUTPUT_PATH"
- type: script
commands:
- command -v ic-wasm >/dev/null 2>&1 || { echo >&2 'ic-wasm not found. To install ic-wasm, see https://github.com/dfinity/ic-wasm \n'; exit 1; }
{{#if metadata}}
{{#each metadata}}
- ic-wasm "$ICP_WASM_OUTPUT_PATH" -o "${ICP_WASM_OUTPUT_PATH}" metadata "{{ name }}" -d "{{ value }}" --keep-name-section
{{/each}}
{{/if}}
icp.yaml
canisters:
- name: backend
recipe:
type: ./recipes/rust-example-metadata.hbs
configuration:
package: my-backend-crate
metadata:
- name: "crate:version"
value: "1.0.0"
- name: "build:profile"
value: "release"

Default Values

Use {{#if}} with {{else}} for defaults, refer to the examples above.

Testing Recipes

Test your recipe by viewing the expanded configuration:

Terminal window
icp project show

This shows exactly what your recipe produces after template expansion.

Verify it works end-to-end:

Terminal window
icp build
icp deploy

Sharing Recipes

Within a project

Store recipes in your project’s recipes/ directory and reference with relative paths:

icp.yaml
canisters:
- name: canister1
recipe:
type: ./recipes/my-recipe.hbs
configuration:
package: my-crate1
- name: canister2
recipe:
type: ./recipes/my-recipe.hbs
configuration:
package: my-other-crate

Across Projects

Host on a web server or GitHub and reference with URL and sha256 hash:

recipe:
type: https://example.com/recipes/my-recipe.hb.yaml
sha256: <sha256-hash-of-file>
configuration:
name: my-canister

Generate the hash:

Terminal window
sha256sum recipes/my-recipe.hb.yaml

Publishing to the Registry

To contribute recipes to the official registry at github.com/dfinity/icp-cli-recipes:

  1. Fork the repository
  2. Add your recipe following the contribution guidelines
  3. Submit a pull request

Recipe Examples

For examples of recipes, you can check out github.com/dfinity/icp-cli-recipes.

Best Practices

  • Keep recipes focused — One recipe per build pattern
  • Document configuration options — Include comments or a README
  • Provide sensible defaults — Use conditionals to make options optional
  • Test thoroughly — Verify recipes work across different projects
  • Version carefully — Use semantic versioning for published recipes

Next Steps

Browse all documentation →