Skip to main content

1.6 Managing canisters

Overview

Now that you have canisters deployed on the mainnet, the next step is to learn how to manage those canisters. Managing a canister includes workflows such as obtaining information, setting an identity as the canister's owner, and deleting a canister. You'll dive into these different workflows and more in this guide to assure that you have the core fundamentals of how to maintain canisters.

The ICP management canister

To enable canister management, the the management canister exposes management functionality to end users and other canisters. While it is titled the 'management canister', it is not actually a canister that exists with an isolated state, Wasm code, etc; it is a facade. It is important to understand the management canister's role, however, in order to understand some of the advanced ICP features, such as threshold ECDSA and integrations like Bitcoin.

Management operations such as updating a canister, creating a canister, and stopping or starting a canister, are sent to the management canister and then executed. When executed, the messages are sent to the relevant subnet and intercepted by the execution environment, which triggers the execution of the operation.

Prerequisites

Before you start, verify that you have set up your developer environment according to the instructions in 0.3 Developer environment setup.

You will be building off of the previous three modules, 1.3 Developing your first dapp, 1.4 Acquiring and using cycles, and 1.5 Deploying canisters. You will need to have followed along and completed the steps outlined in these modules to complete this tutorial.

Obtaining a canister's ID

Each canister has a unique identifier that can be used to interact with the canister. These unique IDs can be used to access the canister's frontend or Candid UI in a web browser, such as when you accessed the frontend of your poll dapp in the last tutorial. Having a canister's ID is also important for executing management functions of the canister, such as setting ownership for the canister.

As an example, to get the canister ID of your poll_backend canister that's been deployed on the mainnet by using the command:

dfx canister id poll_backend --network ic

This command will return the canister's ID, which will look something like this:

bkyz2-fmaaa-aaaaa-qaaaq-cai

If you want to get the canister ID for a locally deployed canister, simply omit the --network ic flag, such as:

dfx canister id poll_backend

Obtaining canister information

Next, to obtain some information about your canister, such as the canister's controller(s) and the Wasm module hash, use the command:

dfx canister info poll_backend

The output will be returned, such as:

Controllers: bnz7o-iuaaa-aaaaa-qaaaa-cai x4d3z-ufpaj-lpxs4-v7gmt-v56ze-aub3k-bvifl-y4lsq-soafd-d3i4k-fqe
Module hash: 0xf8680eb74022a1079012b7e9c644d1156580002a6126305791811533d3fd6f17

Adding an identity as a controller of a canister

To add another identity as an additional controller of the canister, first, let's create a new identity:

dfx identity new ControllerExample

Then, get the principal value for this new identity with the command:

dfx identity use ControllerExample
dfx identity get-principal

The output will resemble the following:

lalyb-uhvmt-p7ubs-u5t7l-hce6v-lp7c5-dlmj5-wi2gc-depab-wtgi3-pae

Copy this value. You'll then use the dfx canister update-settings command to add this principal to be a controller of your poll_backend canister, but first, you need to switch back to your previously created identity, since only existing controllers can add new controllers:

dfx identity use DeveloperJourney

Then, use the dfx canister update-settings command:

dfx canister update-settings poll_backend --add-controller PRINCIPAL_ID

For example, to use your principal value from above:

dfx canister update-settings poll_backend --add-controller lalyb-uhvmt-p7ubs-u5t7l-hce6v-lp7c5-dlmj5-wi2gc-depab-wtgi3-pae

This command adds the principal lalyb-uhvmt-p7ubs-u5t7l-hce6v-lp7c5-dlmj5-wi2gc-depab-wtgi3-pae as an additional controller of your poll_backend canister. To confirm that this principal has been added, you can run the dfx canister info command again:

dfx canister info poll_backend

Now you can see this principal listed as a controller:

Controllers: bnz7o-iuaaa-aaaaa-qaaaa-cai lalyb-uhvmt-p7ubs-u5t7l-hce6v-lp7c5-dlmj5-wi2gc-depab-wtgi3-pae x4d3z-ufpaj-lpxs4-v7gmt-v56ze-aub3k-bvifl-y4lsq-soafd-d3i4k-fqe
Module hash: 0xf8680eb74022a1079012b7e9c644d1156580002a6126305791811533d3fd6f17

You can remove this principal with the command:

dfx canister update-settings poll_backend --remove-controller PRINCIPAL_ID

Alternatively, you can use the --set-controller flag instead of the --add-controller flag. If any controllers are set using the --set-controller flag, any other existing controllers will be removed. For example, re-run the command above, but use the --set-controller flag instead:

dfx canister update-settings poll_backend --set-controller lalyb-uhvmt-p7ubs-u5t7l-hce6v-lp7c5-dlmj5-wi2gc-depab-wtgi3-pae

You will be warned that you'll be removing yourself as a controller, since you're setting the ControllerExample identity as the sole controller, while you're using the DeveloperJourney identity. Accept this warning.

Then, rerun the dfx canister info poll_backend command. The output should now be:

Controllers: lalyb-uhvmt-p7ubs-u5t7l-hce6v-lp7c5-dlmj5-wi2gc-depab-wtgi3-pae
Module hash: 0xf8680eb74022a1079012b7e9c644d1156580002a6126305791811533d3fd6f17

Managing the running state of a canister

Once a canister has been deployed, it can receive and process requests from other canisters or end-users. When a canister is able to send requests and receive replies, the canister is in a Running state.

Canisters are placed in the Running state by default, but there may be situations where a canister needs to be temporarily or permanently stopped. For example, before a canister is upgraded it may be stopped to ensure proper message handling if the message is in progress or should be rolled back. Stopping a canister is also a requirement if the canister is going to be deleted.

To check the current state of all canisters, you can use the command:

dfx canister status --network ic --all

Or, you can check the status of a single canister by specifying it's name, such as:

dfx canister status poll_backend --network ic 

The output of the command will resemble the following:

Canister status call result for poll_backend.
Status: Running
Controllers: lalyb-uhvmt-p7ubs-u5t7l-hce6v-lp7c5-dlmj5-wi2gc-depab-wtgi3-pae
Memory allocation: 0
Compute allocation: 0
Freezing threshold: 2_592_000
Memory Size: Nat(2363181)
Balance: 3_100_000_000_000 Cycles
Reserved: 0 Cycles
Reserved Cycles Limit: 5_000_000_000_000 Cycles
Module hash: 0xf8680eb74022a1079012b7e9c644d1156580002a6126305791811533d3fd6f17

Stopping a canister works in a similar fashion. For example, to stop a single canister, run the command:

dfx canister stop poll_backend --network ic 

Or, to stop all canisters, use the command:

dfx canister stop --network ic --all

This command displays output similar to the following:

Stopping code for canister poll_backend, with canister_id 5o6tz-saaaa-aaaaa-qaacq-cai
Stopping code for canister poll_frontend, with canister_id 5h5yf-eiaaa-aaaaa-qaada-cai

Then you can verify that the canisters have been stopped by rerunning the dfx canister status command:

dfx canister status --network ic poll_backend

Which should return:

Canister status call result for poll_backend.
Status: Stopped
Controllers: lalyb-uhvmt-p7ubs-u5t7l-hce6v-lp7c5-dlmj5-wi2gc-depab-wtgi3-pae
Memory allocation: 0
Compute allocation: 0
Freezing threshold: 2_592_000
Memory Size: Nat(2363181)
Balance: 3_100_000_000_000 Cycles
Reserved: 0 Cycles
Reserved Cycles Limit: 5_000_000_000_000 Cycles
Module hash: 0xf8680eb74022a1079012b7e9c644d1156580002a6126305791811533d3fd6f17

Then to start the canister again, run the command:

dfx canister start --network ic --all

This will return a confirmation such as:

Starting code for canister poll_backend, with canister_id 5o6tz-saaaa-aaaaa-qaacq-cai
Starting code for canister poll_frontend, with canister_id 5h5yf-eiaaa-aaaaa-qaada-cai

Checking the cycles balance of a canister

To check a canister's cycles balance, you must be the controller of the canister. The cycles balance can be seen in the output of the dfx canister status command, such as:

dfx canister status 5o6tz-saaaa-aaaaa-qaacq-cai --network ic

The output will resemble the following, where the value Balance refers to the main cycles balance and Reserved refers to the reserved cycles balance:

Canister status call result for 5o6tz-saaaa-aaaaa-qaacq-cai.
Status: Stopped
Controllers: lalyb-uhvmt-p7ubs-u5t7l-hce6v-lp7c5-dlmj5-wi2gc-depab-wtgi3-pae
Memory allocation: 0
Compute allocation: 0
Freezing threshold: 2_592_000
Memory Size: Nat(2363181)
Balance: 3_100_000_000_000 Cycles
Reserved: 0 Cycles
Reserved Cycles Limit: 5_000_000_000_000 Cycles
Module hash: 0xf8680eb74022a1079012b7e9c644d1156580002a6126305791811533d3fd6f17

Topping up a canister's cycles balance

Depositing cycles into a canister's cycles balance is known as 'topping up' the canister's balance. For production canisters, which are consistently using cycles over time to pay for the canister's resources, topping up the canister is required, routine maintenance.

While you must be a canister's controller in order to view the cycles balance of the canister, anyone can top up a canister.

There are a few ways to top up a canister:

  • Using ICP in your account.
  • Using cycles in your cycles wallet.
  • Using the NNS dapp web UI.
  • Using a third-party service such as CycleOps.

This tutorial will cover the first two workflows. For more information on using a third-party service for cycles management, please see here.

Setting the reserved cycles limit

When a canister allocates storage bytes, the system may reserve some amount of cycles by moving them from the main cycles balance of the canister to the reserved cycles balance. These reserved cycles are used for future storage payments as explained here.

You can control the maximum amount of reserved cycles by setting the reserved cycles limit:

dfx canister update-settings 5o6tz-saaaa-aaaaa-qaacq-cai --reserved-cycles-limit 42 --network ic
dfx canister status 5o6tz-saaaa-aaaaa-qaacq-cai --network ic

The first command sets the reserved cycles limit to 42 and the second command queries the canister status and shows the following output:

Canister status call result for 5o6tz-saaaa-aaaaa-qaacq-cai.
...
Reserved: 0 Cycles
Reserved Cycles Limit: 42 Cycles
...

Once the reserved cycles balances reaches the reserved cycles limit, the system is going to fail all operations that require cycle reservation. The error message might look like this:

Canister cannot grow memory by 65536 bytes due to its reserved cycles limit. The current limit (42) would be exceeded by 1000.

Such errors can be fixed by increasing the reserved cycles limit.

Using ICP

If you currently have a balance of ICP tokens within your dfx ledger account ID, you can use the dfx ledger top-up command to automatically convert that ICP into cycles and deposit it into the specified canister, for example:

dfx ledger top-up 5o6tz-saaaa-aaaaa-qaacq-cai --amount 2.7 --network ic

Using a cycles wallet

If you already have converted some ICP into cycles and filled a cycles wallet, you can send cycles from that wallet to the canister with the dfx canister deposit-cycles command, such as:

dfx canister deposit-cycles 1000000 jqylk-byaaa-aaaal-qbymq-cai --network ic

Getting cycles back from a canister

To withdraw cycles from a canister, the canister must be deleted. The cycles will be returned to the cycles wallet associated with the canister's controller principal.

First, check the cycles balance of your identity:

dfx wallet balance

This will return your cycles balance, such as:

90.700 TC (trillion cycles).

Then, you can stop and delete the canister with the commands:

dfx canister stop jqylk-byaaa-aaaal-qbymq-cai --network ic
dfx canister delete jqylk-byaaa-aaaal-qbymq-cai --network ic

The output of the dfx canister delete command will return information regarding the cycles withdraw:

Beginning withdrawal of cycles to canister jqylk-byaaa-aaaal-qbymq-cai; on failure try --no-wallet --no-withdrawal.
Setting the controller to identity principal.
Installing temporary wallet in canister poll_backend to enable transfer of cycles.
Attempting to transfer 3089393970000 cycles to canister jqylk-byaaa-aaaal-qbymq-cai.
Successfully withdrew 3089393970000 cycles.
Deleting canister poll_backend, with canister_id jqylk-byaaa-aaaal-qbymq-cai

To confirm that the cycles were withdrawn properly, check your cycles wallet balance again with dfx wallet balance. The balance should be increased, such as:

93.789 TC (trillion cycles).

Setting the canister's freezing threshold

In the output of the dfx canister status command, you may have noticed the freezing threshold value. The freezing threshold is a value defined in seconds, which is used to calculate how many cycles a canister must retain in its cycles balance. This calculation is based off of the canister's memory consumption. The default freezing threshold value is 2_592_000s, which corresponds to 30 days.

The freezing threshold is important because if a canister runs out of cycles, it will be uninstalled. The freezing threshold protects it from deletion, since if the cycles balance dips below the threshold, the canister will stop processing any new requests; however, it will continue to reply to existing requests.

For example, to set a freezing threshold for your poll_backend canister, use the command:

dfx canister update-settings poll_backend --freezing-threshold 3472000 

Then, you can confirm that this threshold has been set by running the dfx canister status poll_backend --network ic command again:

Canister status call result for poll_backend.
Status: Stopped
Controllers: lalyb-uhvmt-p7ubs-u5t7l-hce6v-lp7c5-dlmj5-wi2gc-depab-wtgi3-pae
Memory allocation: 0
Compute allocation: 0
Freezing threshold: 3_472_000
Memory Size: Nat(2363181)
Balance: 3_100_000_000_000 Cycles
Module hash: 0xf8680eb74022a1079012b7e9c644d1156580002a6126305791811533d3fd6f17

Deleting a canister

The last portion of canister management that will be covered in this guide is deleting canisters. To delete a single canister, first you need to stop the canister with the command:

dfx canister stop poll_frontend --network ic  

Then you can run the command:

dfx canister delete poll_frontend --network ic 

When a canister is deleted, the canister's code, state, and canister ID are removed. Canisters must be stopped before they can be deleted. Alternatively, all canisters for a project can be deleted with the commands:

dfx canister stop --network ic  --all
dfx canister delete --network ic --all

Need help?

Did you get stuck somewhere in this tutorial, or feel like you need additional help understanding some of the concepts? The ICP community has several resources available for developers, like working groups and bootcamps, along with our Discord community, forum, and events such as hackathons. Here are a few to check out:

Next steps

In the next step of our developer journey, you'll explore upgrading canisters, storage, and persistence.