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:
- 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--authorigin 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
--authorigin determines the derived principal (see Pitfall 2).
- Say: “I’ll link the CLI against
- 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
icpon$PATH— see theicp-cliskill to install. Verify withicp --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-cliskill), then return here.
Step 1 — Link the CLI to your engine identity (once per machine)
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.orgas<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.
-
--authmust 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
Step 2 — Name the app (and give it an icon) in the console (recommended)
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_URLand__META_ICON_PATHonly from it, and the “Open” button targets it.__META_BASE_URL— an absolutehttps://URL, set on the main canister (e.g. the frontend canister’s URLhttps://<frontend-canister-id>.icp.net, or a custom domain). When present and valid, it is the URL the “Open” button opens; when absent or nothttps, the “Open” button falls back to the main canister’s gateway URL. It is also the base that__META_ICON_PATHresolves against.__META_ICON_PATH— the path to the app icon, resolved against__META_BASE_URLto form the icon the console renders (e.g./favicon.svg→https://<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_CANISTERmust be the exact string"true". - They are applied during
icp deploy(the “Setting environment variables” step). After deploy, confirm withicp 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_PATHalone does nothing. __META_BASE_URLmust parse as an absolutehttps://URL. A bare host, anhttp://URL, or adata:/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_PATHis 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 adata: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_URLtohttps://<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 ictargets mainnet (the engine runs on an IC subnet);--subnet <subnet-id>pins the deploy to your engine’s subnet. Confirm the exact flags withicp deploy --helpbefore 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 deployoutput 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_PROJECTname with their__META_NAMElabels, and the main canister shows an “Open” button — instead of bare principal rows. With__META_BASE_URL+__META_ICON_PATHset, 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
- 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). - Wrong
--authorigin. 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 defaulthttps://opencloud.org, ask the user for the exact URL they sign in with and relink. - 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.
- 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. - Using
dfx. This ecosystem usesicp, neverdfx. The correct sequence isicp identity link web <name> --auth <console-origin>(Step 1), thenicp deploy -e ic --subnet <subnet-id>(Step 3). See theicp-cliskill. - 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. - Wrong
__META_MAIN_CANISTERvalue. 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. - 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. - 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 absolutehttps://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.)
Related Skills
- 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.