| title | description |
|---|---|
Local Development |
Day-to-day edit-build-deploy workflow for developing and testing canisters on a local ICP network. |
This guide covers the day-to-day development workflow with icp-cli.
Local development follows a simple loop:
Edit code → Build → Deploy → Test → Repeat
Start the local network in the background:
icp network start -dVerify it's running:
icp network pingAfter editing your source code, deploy the changes:
icp deployThis rebuilds and redeploys all canisters. Deploy specific canisters:
icp deploy my-canisterTip: icp deploy always builds first. If you want to verify compilation before deploying, run icp build separately.
Call methods on your canister:
icp canister call my-canister method_name '(arguments)'Example:
icp canister call backend get_user '("alice")'For read-only methods, use --query for faster uncertified responses:
icp canister call backend get_user '("alice")' --queryManaged networks include a proxy canister that forwards calls with cycles attached. This is useful for testing methods that require cycles:
icp canister call my-canister method '(args)' \
--proxy $(icp network status --json | jq -r .proxy_canister_principal) \
--cycles 1TThe proxy canister's principal is shown in icp network status output.
List canisters configured in this environment (the local environment is the default, targeting your local network):
icp canister listView the effective project configuration:
icp project showDeploy all canisters:
icp deployDeploy specific canisters:
icp deploy frontend
icp deploy backendBuild without deploying (for verification):
icp build # Build all
icp build frontend # Build specific canisterWeb frontends on the Internet Computer are served by asset canisters — pre-built canisters maintained by DFINITY that serve static files (HTML, JS, CSS, images) over HTTP.
The @dfinity/asset-canister recipe deploys this pre-built canister and syncs your frontend files to it:
canisters:
- name: frontend
recipe:
type: "@dfinity/asset-canister"
configuration:
dir: dist # Your built frontend filesDeploy and access your frontend:
icp network start -d
icp deployOpen your browser to http://<frontend-canister-id>.localhost:8000/ (the canister ID is shown in the deploy output).
This section applies when your frontend needs to call backend canisters. If your frontend is purely static, you can skip this.
When a frontend calls a backend canister, it needs two things:
- The backend's canister ID — to know which canister to call
- The network's root key — to verify response signatures
Asset canisters solve this automatically via a cookie named ic_env:
- During
icp deploy, canister IDs are injected asPUBLIC_CANISTER_ID:*canister environment variables - The asset canister serves these variables plus the network's root key via the
ic_envcookie - Your frontend reads the cookie using
@icp-sdk/coreto get canister IDs and root key
This works identically on local networks and mainnet — your frontend code doesn't need to change between environments.
See Canister Discovery for implementation details.
When developing a frontend that calls backend canisters, you have two options:
| Approach | Best for | Trade-offs |
|---|---|---|
| Deploy and access asset canister | Testing production-like behavior | No hot reload; must redeploy on every change |
| Use a local dev server | Fast iteration during development | Requires manual configuration |
Deploy all canisters and access the frontend through the asset canister:
icp deployOpen http://<frontend-canister-id>.localhost:8000/
The asset canister automatically sets the ic_env cookie with canister IDs and the network's root key.
Limitation: No hot module replacement. You must run icp deploy frontend after every frontend change.
For hot reloading, run a dev server (Vite, webpack, etc.) that serves your frontend locally. Since your dev server isn't the asset canister, you need to configure it to provide the ic_env cookie.
Key insight: You only need to deploy the backend canister — the frontend canister isn't needed since your dev server serves the frontend.
icp deploy backend # Only deploy backend
npm run dev # Start your dev serverWhen using a dev server, configure it to:
- Fetch canister IDs and root key from the CLI at startup
- Set the
ic_envcookie with these values (mimics what asset canisters do) - Proxy
/apirequests to the target network
See the frontend-environment-variables example for a complete Vite configuration.
Workflow:
icp network start -d # Start local network
icp deploy backend # Deploy backend canister
npm run dev # Start dev server (fetches IDs automatically)Important: After icp network stop and restart, the dev server will automatically fetch new canister IDs on next startup.
- hello-world template — The template from
icp newshows the complete pattern for reading theic_envcookie. This is the simplest starting point. - frontend-environment-variables example — A detailed Vite setup showing dev server configuration: fetching canister IDs and root key via CLI, setting the
ic_envcookie, and using@icp-sdk/coreto parse environment variables.
To start fresh with a clean network:
# Stop the current network
icp network stop
# Start a new network (previous state is discarded)
icp network start -dThen redeploy your canisters:
icp deployCheck network status:
icp network statusView network details as JSON:
icp network status --jsonExample output for a local managed network:
{
"managed": true,
"api_url": "http://localhost:8000",
"gateway_url": "http://localhost:8000",
"candid_ui_principal": "be2us-64aaa-aaaaa-qaabq-cai",
"proxy_canister_principal": "bd3sg-teaaa-aaaaa-qaaba-cai",
"root_key": "308182..."
}| Field | Description |
|---|---|
managed |
Whether icp-cli controls this network's lifecycle |
api_url |
Endpoint for canister calls |
gateway_url |
Endpoint for browser access to canisters |
candid_ui_principal |
Candid UI canister for testing (managed networks only) |
proxy_canister_principal |
Proxy canister for forwarding calls with cycles (managed networks only) |
root_key |
Network's root key for verifying responses |
For connected networks (like ic), candid_ui_principal and proxy_canister_principal are omitted.
Stop the network when done:
icp network stopBuild fails with "command not found"
A required tool is missing. See the Installation Guide for:
- Rust toolchain — If error mentions
cargoorrustc - Motoko toolchain — If error mentions
mocormops - ic-wasm — If error mentions
ic-wasm
Network connection fails
Check if the network is running:
icp network pingIf not responding, restart:
icp network stop
icp network start -dDeployment fails
- Verify the build succeeded:
icp build - Check network health:
icp network ping
Frontend can't find canister IDs
If using a dev server, ensure you've deployed the backend before starting:
icp deploy backend
npm run dev # Start after deployIf accessing the asset canister directly, check that you're using the correct URL format: http://<canister-id>.localhost:8000/
- Canister Discovery — How canisters find each other
- Deploying to Mainnet — Go live with your canisters