Infrastructure

Deploy to Cloud Engine

Deploys an already-built Internet Computer project to a user's own cloud engine (an OpenCloud / control-panel engine, administered from a web console). Covers verifying the icp CLI, linking the user's console identity to the CLI with `icp identity link web`, defaulting the console origin to https://opencloud.org (overridable when the user signs in to a different console), obtaining the engine's subnet id (asking the user when it is unknown), running `icp deploy` against that subnet, and tagging the canisters with `__META_*` environment variables so the engine console shows a named app with labelled backend/frontend canisters and an app icon. Use when a developer wants to ship an app to their cloud engine, mentions a cloud engine, OpenCloud, an engine subnet id, linking the icp CLI to an engine console, or giving a deployed app a name or icon in the console. Do NOT use for a general mainnet deploy with no specific engine or subnet (use the icp-cli skill) or for writing canister code.

Skill ID
deploy-to-cloud-engine
Category
Infrastructure
License
Apache-2.0
Compatibility
icp-cli >= 0.3.0 (commands verified against 0.3.0), a cloud engine console account, a browser for the Internet Identity sign-in
Last updated
Source

Trust note. This page is a static, pre-rendered mirror of dfinity/icskills/skills/deploy-to-cloud-engine/SKILL.md. The canonical source is the Git commit it was built from. Licensed Apache-2.0.

Deploy to Cloud Engine

What This Is

A cloud engine is a user-owned slice of Internet Computer capacity, administered from a web console (by default https://opencloud.org). Each engine runs on a single subnet. This skill takes a project that already builds and gets it deployed onto that engine, from a coding agent.

This skill only covers the cloud-engine-specific steps: linking the CLI to the engine’s console identity, and a subnet-targeted deploy. For everything else about the CLI (icp.yaml, recipes, environments, bindings, identities), load the icp-cli skill.

Before running any icp command you are unsure of, run icp <subcommand> --help (e.g. icp identity link --help, icp deploy --help) to confirm the command and flags exist. Do not infer flags. Authoritative reference: https://cli.internetcomputer.org/llms.txt

What You Need

Two values. Look for them first in icp.yaml or earlier in the conversation. One has a default; the other you must ask for:

  1. Console origin — the URL the user signs in to their cloud engine console with. Defaults to https://opencloud.org (the main OpenCloud console). It is used as the --auth origin in Step 1 so the linked CLI identity derives the same principal that administers the engine. Use the default, but say so and give the user a chance to override before linking:
    • Say: “I’ll link the CLI against https://opencloud.org, the default console. If you sign in to your engine console at a different URL, tell me now.”
    • Only use a different origin when the user names one — never substitute another URL on your own; the --auth origin determines the derived principal (see Pitfall 2).
  2. Subnet id — the subnet the engine deploys to, required by icp deploy --subnet. There is no default; never guess it. The user finds it on the engine’s App Center / Applications page in the console. If absent, ask and do not proceed without it:
    • Ask: “What is your engine’s subnet id? It is shown on your engine’s App Center / Applications page.”

Record both so you do not re-ask within the session.

Prerequisites

  • icp on $PATH — see the icp-cli skill to install. Verify with icp --version (this skill’s commands are verified against 0.3.0).
  • A project that already builds. If it does not build or package yet, set that up first (see the icp-cli skill), then return here.

The CLI must sign as the same identity that administers the engine — that is the principal you log in to the console with.

First check what already exists:

icp identity list      # names + principals; * marks the active identity

The list does not show which console (if any) an identity was linked against — that cannot be determined from the CLI. Decide like this:

  • Only anonymous (or plain local identities) listed — no web-linked identity exists; run the link command below.
  • An identity the user recognizes as their engine identity (by name or principal) — set it active (below) and skip to Step 2.
  • Unsure — ask the user, or simply relink under a new name; linking again is cheap and safe.

To link, run this, substituting a name the user picks — <your-identity-name> is any local label, not a fixed value (do not hardcode something like my-engine-admin); reuse the same name in every command below:

icp identity link web <your-identity-name> --auth <console-origin>
  • Use https://opencloud.org as <console-origin> unless the user named a different console. Never omit --auth: the flag has a built-in default (https://id.ai) that is not your console and silently derives the wrong principal.

  • The command first waits at a “Press Enter to log in” prompt before anything happens. Run it interactively when you can; in a non-interactive shell (e.g. a background process) pipe a real newline:

    printf '\n' | icp identity link web <your-identity-name> --auth <console-origin>

    Do not redirect stdin from /dev/null — the bare EOF does not satisfy the prompt, and the command sits on “Press Enter to log in” indefinitely with no browser ever opening.

  • After Enter it opens a browser tab. The user completes the Internet Identity sign-in there. Wait for them to confirm before continuing — you cannot complete the sign-in for them.

  • --auth must be the exact console origin (scheme + host), e.g. https://opencloud.org. A mismatched origin derives a different principal, and the engine will reject the deploy as unauthorized.

  • This is a one-time, per-machine step.

Then make it the active identity and verify:

icp identity default <your-identity-name>
icp identity default     # prints the active identity name
icp identity principal   # prints the principal the deploy will sign as

By default, CLI-deployed canisters appear on the engine console’s Applications page as bare rows labelled only by their principal id. A set of canister environment variables makes the console group them into a single named application with readable per-canister labels, an “Open” button, and an icon. Set them once in your project config:

  • __META_PROJECT — the application name. Canisters that share the same value are grouped into one named app, so set an identical value on every canister of the app.
  • __META_NAME — the per-canister display label (e.g. Backend, Frontend).
  • __META_MAIN_CANISTER — the literal string "true" on exactly one canister (the entry point, usually the frontend/asset canister). This marks the app’s main canister: the console reads __META_BASE_URL and __META_ICON_PATH only from it, and the “Open” button targets it.
  • __META_BASE_URL — an absolute https:// URL, set on the main canister (e.g. the frontend canister’s URL https://<frontend-canister-id>.icp.net, or a custom domain). When present and valid, it is the URL the “Open” button opens; when absent or not https, the “Open” button falls back to the main canister’s gateway URL. It is also the base that __META_ICON_PATH resolves against.
  • __META_ICON_PATH — the path to the app icon, resolved against __META_BASE_URL to form the icon the console renders (e.g. /favicon.svghttps://<base>/favicon.svg). Set it on the main canister, alongside __META_BASE_URL.

The icon and “Open” link are read only from the main canister (the one marked __META_MAIN_CANISTER: "true") — __META_BASE_URL / __META_ICON_PATH on any other canister are ignored.

Set them under each canister’s settings.environment_variables — this is valid alongside a recipe. With per-canister canister.yaml files:

# frontend/canister.yaml
name: frontend
recipe:
  type: "@dfinity/asset-canister@v2.2.1"
  configuration:
    build:
      - npm install
      - npm run build
    dir: dist
settings:
  environment_variables:
    __META_PROJECT: "My App"
    __META_NAME: "Frontend"
    __META_MAIN_CANISTER: "true"
    __META_BASE_URL: "https://<frontend-canister-id>.icp.net"
    __META_ICON_PATH: "/favicon.svg"
# backend/canister.yaml
name: backend
recipe:
  type: "@dfinity/motoko@v4.1.0"
  configuration:
    main: src/main.mo
settings:
  environment_variables:
    __META_PROJECT: "My App"
    __META_NAME: "Backend"

For a single inline icp.yaml (canisters defined there directly), put the same settings.environment_variables block under each canister entry. Note the inline form: canisters is an array of {name, recipe, settings} items, not a map keyed by canister name:

# icp.yaml — canisters defined inline
canisters:
  - name: frontend
    recipe: # … as in the canister.yaml example above
    settings:
      environment_variables:
        __META_PROJECT: "My App"
        __META_NAME: "Frontend"
        __META_MAIN_CANISTER: "true"
        __META_BASE_URL: "https://<frontend-canister-id>.icp.net"
        __META_ICON_PATH: "/favicon.svg"
  - name: backend
    recipe: # … as in the canister.yaml example above
    settings:
      environment_variables:
        __META_PROJECT: "My App"
        __META_NAME: "Backend"

Notes:

  • icp-cli merges these with the PUBLIC_CANISTER_ID:<name> variables it injects automatically at deploy time — the asset canister keeps serving and the app keeps working. (Verified against icp-cli 0.3.0.)
  • All values are strings; __META_MAIN_CANISTER must be the exact string "true".
  • They are applied during icp deploy (the “Setting environment variables” step). After deploy, confirm with icp canister settings show <name> -e ic.

Icon specifics (the console builds the icon as __META_BASE_URL + __META_ICON_PATH):

  • Both must be present and on the main canister for an icon to appear — there is no fallback. __META_ICON_PATH alone does nothing.
  • __META_BASE_URL must parse as an absolute https:// URL. A bare host, an http:// URL, or a data: / javascript: value is rejected: the icon then does not render, and the “Open” button falls back to the main canister’s gateway URL (it does not disappear). (The console validates the scheme before using it.)
  • __META_ICON_PATH is a path to an asset your frontend actually serves (e.g. /favicon.svg), not an inline image. The resolved URL is rendered as an <img> src, so it must return an image. Do not put a data: URI here: engine env values are length-capped (≤128 chars observed), so it would not fit, and the field is a path by design.
  • The frontend canister’s id is only known after the first deploy. The usual flow is: deploy once, read the frontend canister id from the output, set __META_BASE_URL to https://<that-id>.icp.net (and __META_ICON_PATH), then re-deploy to apply. If you control a custom domain for the app, you can set it up front instead.

Step 3 — Deploy to the engine’s subnet

From the project root:

icp deploy -e ic --subnet <subnet-id>
  • -e ic targets mainnet (the engine runs on an IC subnet); --subnet <subnet-id> pins the deploy to your engine’s subnet. Confirm the exact flags with icp deploy --help before running if unsure.
  • Deploying consumes capacity on the engine; make sure the engine has room.

Alternative — packaged upload. If the project is distributed as a built .icp package and a direct icp deploy is not available, upload the bundle on the console’s App Center via “Upload a custom app” instead.

Step 4 — Verify

  • The icp deploy output reports the deployed canister ids.
  • The canisters appear on the engine’s Applications page in the console; each canister’s detail view offers an “Open in browser” link.
  • If you set the metadata in Step 2, the canisters are grouped under your __META_PROJECT name with their __META_NAME labels, and the main canister shows an “Open” button — instead of bare principal rows. With __META_BASE_URL + __META_ICON_PATH set, the app also shows its icon (allow for a short console cache delay).
  • A frontend (asset) canister is served at https://<frontend-canister-id>.icp.net.

Report the deployed canister ids (and the frontend URL, if any) back to the user.

Common Pitfalls

  1. Sign-in not completed. Running icp identity link web … but not finishing the Internet Identity sign-in in the browser leaves the CLI unlinked; later commands fail with authorization errors. Re-run and wait for the user to confirm the browser flow finished. If no browser ever opened, the command is stalled at the “Press Enter to log in” prompt — relaunch with a piped newline, printf '\n' | icp identity link web …, never < /dev/null (see Step 1).
  2. Wrong --auth origin. Using any URL other than the console origin the user signs in with derives a different principal, and the engine rejects the deploy as not authorized. Relink with the exact console URL. If the deploy is rejected as unauthorized after linking against the default https://opencloud.org, ask the user for the exact URL they sign in with and relink.
  3. Guessing the subnet id. Never invent it — the deploy fails or targets the wrong subnet. It is on the engine’s App Center / Applications page; ask the user.
  4. Deploying with the anonymous identity. The default local identity is anonymous and is not the engine admin. You must link and icp identity default <your-identity-name> first.
  5. Using dfx. This ecosystem uses icp, never dfx. The correct sequence is icp identity link web <name> --auth <console-origin> (Step 1), then icp deploy -e ic --subnet <subnet-id> (Step 3). See the icp-cli skill.
  6. Skipping the app metadata. Without __META_PROJECT (Step 2), the canisters still deploy and work but render as bare, unnamed principal rows in the console. Setting __META_* is what produces a named app with labelled canisters and an “Open” button.
  7. Wrong __META_MAIN_CANISTER value. It is matched as the exact string "true". A boolean, "True", or marking more than one canister means no (or the wrong) “Open” button. Mark exactly one entry-point canister.
  8. Inventing an icon variable. The icon variable is __META_ICON_PATH (a path resolved against __META_BASE_URL). Do not guess __META_ICON, __META_LOGO, or __META_ICON_LINK — they are ignored, so the icon silently never appears.
  9. Icon set on the wrong canister, or without a base URL. The icon is read only from the main canister and needs both __META_BASE_URL (a valid absolute https:// URL) and __META_ICON_PATH. Setting the icon path on a side canister, omitting the base URL, or giving a non-https / data: base means no icon renders. (The “Open” button still works — it falls back to the main canister’s gateway URL — so a bad base URL costs the icon and the custom Open URL, not the button.)
  • icp-cli — general icp CLI usage (icp.yaml, recipes, environments, bindings, identities). Load it for anything beyond this cloud-engine deploy flow.
  • internet-identity — details of the Internet Identity sign-in that Step 1 triggers in the browser.